Giter Club home page Giter Club logo

shimm's Introduction



쉼 - 고단했던 하루에 고요함을 선물해보세요.


스크린샷 2024-04-24 오후 3 38 11

쉼 배포 링크 및 테스트 계정

Shimm

일반 유저
ID: [email protected]
PW: 11111111
관리자 계정
ID: [email protected]
PW: 11111111

쉼 서비스 소개


고단했던 하루에 고요함을 선물해보세요.

마음을 가다듬고 내면의 평화와 깊은 휴식을 찾아가는 여정을 지원합니다.

쉼 팀원 소개


팀로고 사진

멋쟁이 사자처럼 FES 9기 2팀 < 2thetop >

정진욱 TEAM LEADER 민다인 정기호
명상 서비스 구현 및 총괄 로그인, 소셜로그인,
회원가입 구현 및 디자인 총괄
커뮤니티, 마이페이지, 캘린더

개발 일정

  • 아이디어 구상 ( 2024.03.29 - 2024.04.01 )
  • 프로젝트 기획 ( 2024.04.02 - 2024.04.03 )
  • 화면 설계 및 디자인 ( 2024.04.03 - 2024.04.10 )
  • 코드 컨벤션 설정, 컴포넌트 분리 ( 2024.04.05 )
  • 개발 ( 2024.04.08 - 2024.04.22 )
  • 코드 보완 및 에러 수정, 개발 내용 문서화 ( 2024.04.23 - 2024.04.24 )

    스크린샷 2024-04-24 오후 8 47 38

개발 환경 및 기술 스택

Tools

Git Github Discord Notion
git logo github logo Discord logo
icon

Front-end

Html CSS JavaScript React Zustand Styled-
Components
Prettier React-
Query
KaKaoLogin
Html css
icon
image
styled-components icon
icon
rq

요구사항 정의서

스크린샷 2024-04-24 오후 2 55 49

컨벤션

Code Convention

🚨 웹 표준, 시맨틱 마크업 준수하기, 웹 접근성 고려하기**

1. Naming
    - 파일명
        - 컴포넌트 : Component.jsx (파스칼케이스)
        - 훅, 상태관리(Store) : useCustomHook.mjs
    - import : `import ReservationCard from '@폴더명/파일명';`
    - 변수명 : camelCase
    - prop명 : DOM 컴포넌트에서 사용하는 prop명만 피하기
2. 폴더구조
    - 관련 있는 컴포넌트(부모-자식) 별로 폴더를 만들어서 그 안에  jsx파일과 .style.jsx파일 생성
    - 공통 스타일은 styles 폴더 안에서 관리
3. 선언
    - export : (혹시나) 여러 개를 export 해야 할 경우 맨 밑에서 객체 형태로 한 번에
4. 정렬
<Foo superLongParam="bar" anotherSuperLongParam="baz" />

<Foo superLongParam="bar" anotherSuperLongParam="baz">
  <Quux />
</Foo>

// 조건 {showButton && (
<button />
)} {showButton && <button />} {someReallyLongConditional &&
anotherLongConditional && (
<Foo superLongParam="bar" anotherSuperLongParam="baz" />
) } {someConditional ? (
<Foo />
) : (
<Foo superLongParam="bar" anotherSuperLongParam="baz" />
)}
5. **작은따옴표**
6. Spacing
    - prop은 붙이고 객체는 중괄호 앞/뒤 띄어쓰기
 <Foo bar={baz} />
             <Hello name={{ firstname: 'John', lastname: 'Doe' }} />;
7. Props
    - prop이 불린형일 때는 값을 생략하면 default=true
<Foo hidden />
    - 이미지의 alt 속성은 반드시 작성
<img src="hello.jpg" alt="" />
8. propType 위치
    - 컴포넌트 아래, export 위
    - defaultProps
// bad
function SFC({ foo, bar, children }) {
  return (
    <div>
      {foo}
      {bar}
      {children}
    </div>
  );
}
SFC.propTypes = {
  foo: PropTypes.number.isRequired,
  bar: PropTypes.string,
  children: PropTypes.node,
};

// good
function SFC({ foo, bar, children }) {
  return (
    <div>
      {foo}
      {bar}
      {children}
    </div>
  );
}
SFC.propTypes = {
  foo: PropTypes.number.isRequired,
  bar: PropTypes.string,
  children: PropTypes.node,
};
SFC.defaultProps = {
  bar: '',
  children: null,
};
    - prop-type 설정
MyComponent.propTypes = {
  // 특정 JS 타입임을 선언(해당 속성이 전달되지 않아도 됨)
  optionalArray: PropTypes.array,
  optionalBool: PropTypes.bool,
  optionalFunc: PropTypes.func,
  optionalNumber: PropTypes.number,
  optionalObject: PropTypes.object,
  optionalString: PropTypes.string,
  optionalSymbol: PropTypes.symbol,

  // 모든 종류의 자식 요소(리액트 엘리먼트, 문자, 숫자, 배열, 불린, null, undefined 등)
  optionalNode: PropTypes.node,

  // React 엘리먼트
  optionalElement: PropTypes.element,

  // React 동적으로 로딩된 엘리먼트
  optionalElementType: PropTypes.elementType,

  // 특정 클래스의 인스턴스
  // 이 경우 JavaScript의 instanceof 연산자를 사용
  optionalMessage: PropTypes.instanceOf(Message),

  // 열거형(enum)으로 처리하여 prop가 특정 값들로 제한되도록 할 수 있음
  optionalEnum: PropTypes.oneOf(['News', 'Photos']),

  // 여러 종류중 하나
  optionalUnion: PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.number,
    PropTypes.instanceOf(Message),
  ]),

  // 특정 타입의 배열
  optionalArrayOf: PropTypes.arrayOf(PropTypes.number),

  // 특정 타입의 프로퍼티 값들을 갖는 객체
  optionalObjectOf: PropTypes.objectOf(PropTypes.number),

  // 지정된 타입의 속성을 가지고 있는 객체(다른 속성이 있어도 됨)
  optionalObjectWithShape: PropTypes.shape({
    color: PropTypes.string,
    fontSize: PropTypes.number,
  }),

  // 지정된 타입의 속성만 가지고 있는 객체(다른 속성이 있으면 안됨)
  optionalObjectWithStrictShape: PropTypes.exact({
    name: PropTypes.string,
    quantity: PropTypes.number,
  }),

  // 위에 있는 모든 구문에 'isRequired'를 연결하면 해당 속성이 필수임을 나타냄
  requiredFunc: PropTypes.func.isRequired,

  // 모든 데이터 타입이 가능한 필수값
  requiredAny: PropTypes.any.isRequired,

  // 사용자 정의 유효성 검사기를 지정
  // 검사 실패 시에는 에러(Error) 객체를 반환해야 함
  customProp: function (props, propName, componentName) {
    if (!/matchme/.test(props[propName])) {
      return new Error(
        `'${componentName}' 컴포넌트의 prop '${propName}' 값 검증 실패.`,
      );
    }
  },

  // 'arrayOf' 와 'objectOf'에 사용자 정의 유효성 검사기 지정
  // 검사 실패 시에는 에러(Error) 객체를 반환해야 함
  // 유효성 검사기는 배열(array) 혹은 객체의 각 키(key)에 대하여 호출됨

  // propValue: 현재 검사 중인 prop의 값(배열이나 객체)
  // key: 현재 검사 중인 prop의 키
  // componentName: 현재 검사 중인 컴포넌트의 이름
  // location: prop이 전달된 위치 ("props" 또는 "context" 중 하나)
  // propFullName: prop의 이름
  customArrayProp: PropTypes.arrayOf(
    function (propValue, key, componentName, location, propFullName) {
      if (!/matchme/.test(propValue[key])) {
        return new Error(
          'Invalid prop `' +
            propFullName +
            '` supplied to' +
            ' `' +
            componentName +
            '`. Validation failed.',
        );
      }
    },
  ),
};
9. 함수 사용
    - 최대한 함수 선언문
    - 콜백 등 필요한 경우는 화살표 함수 사용
10. 괄호
    - 두 줄 이상은 반드시 괄호, 한 줄일 때만 괄호 없이
// good
render() {
  return (
    <MyComponent variant="long body" foo="bar">
      <MyChild />
    </MyComponent>
  );
}

// good, when single line
render() {
  const body = <div>hello</div>;
  return <MyComponent>{body}</MyComponent>;
}
11. 태그
    - 안에 children 없으면 self-closing tag
12. 메서드
    - 괄호 열고 엔터 쳤으면 엔터 후 하나 앞에 tab에서 닫아주기
{
  props.items.map((item, index) => (
    <Item
      key={item.key}
      onClick={event => {
        doSomethingWith(event, item.name, index);
      }}
    />
  ));
}
13. 훅
    - 훅을 맨 위에
    - 그 밑에 변수
    - useEffect 같은 경우는 상황에 맞게

14. 자바스크립트 코드 컨벤션
    - 무조건 const, 바꿀 경우가 있으면 let
Commit Convention

  • 새로운 기능 추가 할때
 ✨feat: XXXX 기능 추가
  • 기능 수정 중
 🚧 cont: XXXX 기능 수정 중
  • 기능 수정 완료
 ✅ update: XXXX 기능 수정 완료
  • 버그 수정
 🐛 fix: XXXX 버그 수정
  • 디자인 수정
 💄 style: XXXX 디자인 수정
  • 리팩토링
 ♻️ refactor: 코드 리팩토링
  • 문서 수정
 📝 docs: README.md 파일 수정
  • config 파일 수정
🔧 conf: XXXX 파일 수정
  • 파일 수정/삭제
 🔥 remove: XXXX 파일 수정/삭제
  • 성능 향상
 ⚡️ performance: 성능 향상
  • 테스트 성공/업데이트
 ✅ passtest: 테스트 성공/업데이트
  • 테스트 실패
 🧪 failtest: 테스트 실패
  • asset 업데이트
 🍱 asset: asset 폴더 업데이트
  • 오타 수정
 ✏️ typos: 오타 수정
  • merge
 🔀 merge: from XXXX to XXXX
  • 배포
🚀 deploy: xxxx 업데이트 후 배포


스타일 가이드

image image image image

플로우 차트

shimm-flowchart

Project Tree

Project Tree

project
📦src
 ┣ 📂assets
 ┃ ...
 ┣ 📂components
 ┃ ┣ 📂animation
 ┃ ┃ ┣ 📜Animation.jsx
 ┃ ┃ ┗ 📜meditation-animation01.json
 ┃ ┣ 📂button
 ┃ ┃ ┣ 📜Button.jsx
 ┃ ┃ ┗ 📜Button.style.jsx
 ┃ ┣ 📂calendar
 ┃ ┃ ┣ 📜Calendar.jsx
 ┃ ┃ ┗ 📜Calendar.style.jsx
 ┃ ┣ 📂input
 ┃ ┃ ┣ 📜Input.jsx
 ┃ ┃ ┗ 📜Input.style.jsx
 ┃ ┣ 📂layout
 ┃ ┃ ┣ 📂footer
 ┃ ┃ ┃ ┣ 📜Footer.jsx
 ┃ ┃ ┃ ┗ 📜Footer.style.jsx
 ┃ ┃ ┣ 📂header
 ┃ ┃ ┃ ┣ 📜Header.jsx
 ┃ ┃ ┃ ┗ 📜Header.style.jsx
 ┃ ┃ ┗ 📂layout
 ┃ ┃ ┃ ┣ 📜Layout.jsx
 ┃ ┃ ┃ ┗ 📜Layout.style.jsx
 ┃ ┣ 📂loading
 ┃ ┃ ┣ 📜Loading.jsx
 ┃ ┃ ┣ 📜Loading.style.jsx
 ┃ ┃ ┗ 📜loading.json
 ┃ ┣ 📂modal
 ┃ ┃ ┣ 📜ModalWindow.jsx
 ┃ ┃ ┗ 📜ModalWindow.style.jsx
 ┃ ┣ 📂result
 ┃ ┃ ┣ 📜Result.jsx
 ┃ ┃ ┗ 📜Result.style.jsx
 ┃ ┣ 📂socialButton
 ┃ ┃ ┣ 📜SocialButton.jsx
 ┃ ┃ ┗ 📜SocialButton.style.jsx
 ┃ ┣ 📂socialLogin
 ┃ ┃ ┗ 📜SocialKakao.jsx
 ┃ ┗ 📂styles
 ┃ ┃ ┗ 📜GlobalStyle.jsx
 ┣ 📂hooks
 ┃ ┣ 📜useClickOutside.mjs
 ┃ ┣ 📜useCustomAxios.mjs
 ┃ ┣ 📜useDetectClose.mjs
 ┃ ┗ 📜useWindowWide.mjs
 ┣ 📂pages
 ┃ ┣ 📂admin
 ┃ ┃ ┣ 📜UploadTheme.jsx
 ┃ ┃ ┗ 📜UploadTheme.style.jsx
 ┃ ┣ 📂auth
 ┃ ┃ ┗ 📜Kakao.jsx
 ┃ ┣ 📂community
 ┃ ┃ ┣ 📂feed
 ┃ ┃ ┃ ┣ 📂create
 ┃ ┃ ┃ ┃ ┣ 📜FeedCreate.jsx
 ┃ ┃ ┃ ┃ ┗ 📜FeedCreate.style.jsx
 ┃ ┃ ┃ ┣ 📂dropdown
 ┃ ┃ ┃ ┃ ┣ 📜FeedDropdown.jsx
 ┃ ┃ ┃ ┃ ┗ 📜FeedDropdown.style.jsx
 ┃ ┃ ┃ ┣ 📂editor
 ┃ ┃ ┃ ┃ ┣ 📜FeedEditor.style.jsx
 ┃ ┃ ┃ ┃ ┣ 📜FeedEditorEdit.jsx
 ┃ ┃ ┃ ┃ ┗ 📜FeedEditorNew.jsx
 ┃ ┃ ┃ ┣ 📂reply
 ┃ ┃ ┃ ┃ ┣ 📜Reply.style.jsx
 ┃ ┃ ┃ ┃ ┣ 📜ReplyItem.jsx
 ┃ ┃ ┃ ┃ ┣ 📜ReplyList.jsx
 ┃ ┃ ┃ ┃ ┗ 📜ReplyNew.jsx
 ┃ ┃ ┃ ┣ 📜Feed.jsx
 ┃ ┃ ┃ ┣ 📜Feed.style.jsx
 ┃ ┃ ┃ ┣ 📜FeedDetail.jsx
 ┃ ┃ ┃ ┣ 📜FeedEdit.jsx
 ┃ ┃ ┃ ┣ 📜FeedList.jsx
 ┃ ┃ ┃ ┣ 📜FeedNew.jsx
 ┃ ┃ ┃ ┗ 📜PostDetail.jsx
 ┃ ┃ ┗ 📜Community.jsx
 ┃ ┣ 📂error
 ┃ ┃ ┣ 📜ErrorPage.jsx
 ┃ ┃ ┗ 📜ErrorPage.style.jsx
 ┃ ┣ 📂home
 ┃ ┃ ┣ 📜Home.jsx
 ┃ ┃ ┣ 📜Home.style.jsx
 ┃ ┃ ┗ 📜HomeCarousel.jsx
 ┃ ┣ 📂meditation
 ┃ ┃ ┣ 📂themeset
 ┃ ┃ ┃ ┣ 📂themeitem
 ┃ ┃ ┃ ┃ ┣ 📜ThemeItem.jsx
 ┃ ┃ ┃ ┃ ┗ 📜ThemeItem.style.jsx
 ┃ ┃ ┃ ┣ 📜ThemeMenu.jsx
 ┃ ┃ ┃ ┣ 📜ThemeSet.jsx
 ┃ ┃ ┃ ┗ 📜ThemeSet.style.jsx
 ┃ ┃ ┣ 📂timer
 ┃ ┃ ┃ ┣ 📜Timer.jsx
 ┃ ┃ ┃ ┗ 📜Timer.style.jsx
 ┃ ┃ ┣ 📂timeset
 ┃ ┃ ┃ ┣ 📜TimeMenu.jsx
 ┃ ┃ ┃ ┣ 📜TimeSet.jsx
 ┃ ┃ ┃ ┗ 📜TimeSet.style.jsx
 ┃ ┃ ┣ 📜Meditation.jsx
 ┃ ┃ ┣ 📜Meditation.style.jsx
 ┃ ┃ ┣ 📜MeditationMain.jsx
 ┃ ┃ ┣ 📜MeditationProgress.jsx
 ┃ ┃ ┗ 📜MeditationRecord.jsx
 ┃ ┣ 📂mypage
 ┃ ┃ ┣ 📂editprofile
 ┃ ┃ ┃ ┣ 📜EditProfile.jsx
 ┃ ┃ ┃ ┗ 📜EditProfile.style.jsx
 ┃ ┃ ┣ 📂myactivity
 ┃ ┃ ┃ ┣ 📜BookmarkedPosts.jsx
 ┃ ┃ ┃ ┣ 📜MyActivity.jsx
 ┃ ┃ ┃ ┣ 📜MyActivity.style.jsx
 ┃ ┃ ┃ ┗ 📜MyPosts.jsx
 ┃ ┃ ┣ 📂myinfo
 ┃ ┃ ┃ ┣ 📜MyInfo.jsx
 ┃ ┃ ┃ ┣ 📜MyInfo.style.jsx
 ┃ ┃ ┃ ┗ 📜MyInfoCheck.jsx
 ┃ ┃ ┣ 📂myrecord
 ┃ ┃ ┃ ┣ 📜MyRecord.jsx
 ┃ ┃ ┃ ┗ 📜MyRecord.style.jsx
 ┃ ┃ ┣ 📜MyPage.jsx
 ┃ ┃ ┗ 📜MyPage.style.jsx
 ┃ ┣ 📂purchase
 ┃ ┃ ┣ 📜Purchase.jsx
 ┃ ┃ ┗ 📜Purchase.style.jsx
 ┃ ┗ 📂users
 ┃ ┃ ┣ 📜Login.jsx
 ┃ ┃ ┣ 📜Login.style.jsx
 ┃ ┃ ┣ 📜SignUp.jsx
 ┃ ┃ ┣ 📜SignUp.style.jsx
 ┃ ┃ ┣ 📜SignUpOneStep.jsx
 ┃ ┃ ┗ 📜SignUpTwoStep.jsx
 ┣ 📂zustand
 ┃ ┣ 📜form.mjs
 ┃ ┣ 📜modal.mjs
 ┃ ┣ 📜themeSelection.mjs
 ┃ ┣ 📜timeSelection.mjs
 ┃ ┣ 📜timer.mjs
 ┃ ┗ 📜user.mjs
 ┣ 📜App.css
 ┣ 📜App.jsx
 ┣ 📜index.css
 ┣ 📜main.jsx
 ┗ 📜routes.jsx


상세 담당 업무

✨ 진욱

역할

  • git 관리
  • 배포
  • QA & 테스트

UI 구현

[ 공통 ]
  • 버튼 컴포넌트
  • 모달 윈도우

[ 회원 ]
  • 명상 설정 페이지

  • 명상 메인 페이지

  • 명상 기록 페이지

  • 명상 테마 구매 페이지

  • 커뮤니티 메인 페이지

  • 커뮤니티 게시글 등록 페이지

  • 커뮤니티 게시글 상세 페이지

  • 마이페이지 나의 활동 페이지

[ 관리자 ]
  • 명상 테마 등록 페이지

기능 구현

[ 공통 ]
  • 헤더 및 명상 시간 선택 드롭다운 메뉴에서 컴포넌트 외부를 클릭하면 비활성화 하는 useClickOutside 훅 설계

[ 로그인 및 회원가입 ]
  • 회원가입 시, 프로필 이미지 등록 및 등록된 이미지 삭제
  • 카카오 로그인(소셜 로그인)
  • 카카오 로그인 API를 이용해 인가 코드를 받아 api 서버에 전달

[ 명상 ]
  • 명상 시간, 테마를 설정한 후 zustand를 이용해 상태관리
    • 명상 이후 명상 완료 시간까지
    • 세션에 저장하여 새로고침 시 설정이 초기화 되는 것을 방지
    • 명상 기록을 저장한 이후 세션에서 데이터 삭제
  • useRef와 setInterval 함수를 이용해 정해진 시간만큼 동작하는 Timer 컴포넌트 개발
  • React Player를 이용, SoundCloud에 저장된 명상 음악 재생
    • 명상 시간 동안 반복 재생
    • 테마 구매 전 1분 미리듣기
  • 명상 기록 저장
  • 명상 유료/무료, 구매 여부에 따라 테마 업데이트
  • 포트원 결제 API를 이용한 유료 테마 결제

[ 커뮤니티 ]
  • 커뮤니티 피드 게시글 작성/삭제/수정/북마크 등록
  • 커뮤니티 댓글 작성 및 삭제
  • react query를 이용해 커뮤니티 피드 및 게시글 댓글 무한스크롤

[ 마이페이지 ]
  • 내가 작성한 글, 내가 북마크 한 글 렌더링
  • 내 정보 수정 - 프로필 사진 변경/삭제

✨ 다인

역할

  • 기획 및 디자인
  • 로그인, 소셜로그인, 회원가입

기획 및 디자인

  • Figma를 이용한 플로우차트, 와이어 프레임 작성
  • Figma를 이용한 반응형 디자인 작성

UI 구현

[ 공통 ]
  • Lottie를 이용한 로딩중 애니메이션
  • 에러 페이지
  • Footer

[ 메인 ]
  • 메인 페이지

[ 명상 ]

= Lottie를 이용한 명상 화면 애니메이션

[ 회원 ]
  • 로그인 페이지
  • 회원가입 페이지 (2 Step)
  • 소셜 로그인 페이지

기능 구현

[ 메인 ]
  • react-swiper 라이브러리를 이용한 메인 비주얼 캐러셀

[ 로그인 및 회원가입 ]
  • Zustand를 이용해 단계별로 데이터를 저장하는 멀티 스텝 폼
  • react-hook-form 을 이용한 회원가입 시 유효성 검증 및 데이터 전송
  • 이메일 중복체크 로직
  • 카카오로 로그인 버튼 클릭 시 Redirect Url로 이동

✨ 기호

역할

  • 커뮤니티
  • 마이페이지
  • 나의 기록 - 캘린더

UI 구현

[ 회원 ]
  • 커뮤니티 메인 페이지
  • 커뮤니티 게시글 등록 페이지
  • 커뮤니티 게시글 상세 페이지
  • 회원정보 확인 페이지
  • 회원정보 수정 페이지
  • 나의 명상 기록 페이지

기능 구현

[ 커뮤니티 ]
  • 커뮤니티 피드 게시글 등록
  • 커뮤니티 댓글 작성

[ 마이페이지 ]
  • 마이페이지 비밀번호 확인 후 일치하면 회원정보 수정 페이지로 이동
  • 회원정보 수정
  • 나의 명상 기록 페이지에 명상 기록 저장 후 캘린더페이지에서 날짜별로 명상 기록 확인


주요 기능 소개

시연 영상

https://youtu.be/AHaA8VEULZM


핵심 코드

  • 모바일 화면에서 드롭 다운 형식으로 작동하는 반응형 헤더와 명상 시간을 설정하는 드롭다운 메뉴에서, 해당 컴포넌트 이외의 영역을 클릭하거나 터치하면 드롭다운 메뉴를 접는 기능을 구현

    useEffect(() => {
      function handleClickOutside(event) {
        if (ref.current && !ref.current.contains(event.target)) {
          onClickOutside();
        }
      }
    
      document.addEventListener('mousedown', handleClickOutside);
    
      return () => {
        document.removeEventListener('mousedown', handleClickOutside);
      };
    }, [ref, onClickOutside]);

  • 카카오 API를 이용해 카카오에서 사용자의 인가 코드를 받아온 후, api 서버에 발급받은 인가코드와 Redirect URI를 전달하여 카카오 로그인 및 회원가입을 구현
    카카오에서 인가코드를 받아 params로 받아오는 것이 핵심

    const KAKAO_URL = `https://kauth.kakao.com/oauth/authorize?client_id=${REST_API_KEY}&redirect_uri=${REDIRECT_URI}&response_type=code`;
    
    const handleLogin = () => {
      window.location.href = KAKAO_URL;
    };
    
    const [searchParams] = useSearchParams();
    const code = searchParams.get('code');
    
    const res = await axios.post('users/login/kakao', {
      code,
      redirect_uri: `${window.location.origin}/auth/kakao`,
    });

  • setInterval 함수를 이용해 사용자가 설정한 시간부터 1초 간격으로 time을 변경 후 렌더링하며 동작하는 타이머를 구현

    (function handleStart() {
      if (!isStarted) {
        formatTime();
        timerRef.current = setInterval(() => {
          setTime(prevTime => prevTime - 1);
        }, 1000);
        setIsStarted(true);
      }
    })();

  • React Player 라이브러리를 이용하여, Sound Cloud에 저장되어 있는 쉼의 자체 제작 명상 음악을 재생, Sound Cloud 플레이어는 반복 재생 기능을 제공하지 않아 음악이 종료될 시점에 다시 처음으로 이동하며 명상 시간 동안 반복 재생할 수 있도록 구현

    function handleProgress(state) {
      const { playedSeconds } = state;
      const remainingTime = duration - playedSeconds;
      const threshold = 3;
    
      if (remainingTime <= threshold) {
        setIsPlaying(false);
        playerRef.current.seekTo(0);
        setIsPlaying(true);
      }
    }
    
    function handleReady() {
      const trackDuration = playerRef.current.getDuration();
      setDuration(trackDuration);
      setIsPlaying(true);
    }
    
    <ReactPlayer
      ref={playerRef}
      url={selectedTheme.music}
      loop={false}
      playing={isPlaying}
      controls={false}
      onReady={handleReady}
      onProgress={handleProgress}
    />;

  • JavaScript CDN으로 포트원 결제 API를 연동하여 KCP 결제 서비스를 통해 유료 테마를 구매할 수 있는 기능 구현,
    테스트 결제가 가능하고, 결제 완료 시 사용자의 테마 목록에서 구매 여부를 렌더링

    const { IMP } = window;
        IMP.init(import.meta.env.VITE_MERCHANT_CODE);
        IMP.request_pay(
          {
            pg: 'kcp',
            pay_method: 'card',
            merchant_uid:
              new Date().getTime() + Math.floor(Math.random() * 1000000),
            name: '테마 결제',
            amount: 1000,
            buyer_name: user.name,
            buyer_tel: user.phone,
            buyer_email: user.email,
          },
          async res => {
            try {
              if (res.success) {
                await axios.post('/orders', {
                  products: [
                    {
                      _id: JSON.parse(sessionStorage.getItem('theme')).state
                        .selectedTheme.id,
                      quantity: 1,
                      extra: { ...res },
                    },
                  ],
                });
    
    
     중략


프로젝트를 마치며 아쉬웠던 점

  • 처음 기획에는 카카오 외에도 구글과 네이버 로그인까지 총 3개의 소셜 로그인을 구현하여 사용자의 편의성을 고려했지만 구현하지 못하여 아쉬움.

  • 웹 접근성을 처음부터 많이 신경쓰기로 기획을 하였지만 시간 관계상 다른 중요 기능에 밀려 생각보다 접근성 중요 비중이 뒤로 밀려 처음 기획단계보다 많이 챙기지 못해 아쉬움.

  • CSS Variable을 사용하고 싶었으나 styled component와 친해지는게 먼저였기 때문에 신경을 쓰지 못한 점이 아쉬움.

  • 다크모드 기능을 구현하지 못해 아쉬움.

  • 성능 최적화 테스트를 충분히 하지 못해 아쉬움.

shimm's People

Contributors

jwjung-99 avatar danggin avatar kihonoluluhawaii avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.