Skip to content

Conversation

@yiseungyun
Copy link
Member

@yiseungyun yiseungyun commented Nov 28, 2024

Note

작성하다보니 양이 많아졌네요..
다 분리해서 PR을 날리면 좋은데 제가 집중력 저하 이슈로 이 작업했다가 저거했다가 하다보니 PR을 나누지 못했습니다. 이 점 양해부탁드립니다. ㅠㅠ 그래도 각 작업 분량이 많지는 않습니다!

관련 이슈 번호

  • 작성자: 이승윤
  • 작성 날짜: 2024.11.28

✅ 체크리스트

  • 코드가 정상적으로 작동하는지 확인했습니다.
  • 주요 변경사항에 대한 설명을 작성했습니다.
  • 코드 스타일 가이드에 따라 코드를 작성했습니다.

🧩 작업 내용

  • 마이페이지 카드 컴포넌트 디자인 수정
  • 모달창 개선
  • 폴더 구조 리팩토링 방향

📝 작업 상세 내역

마이페이지 카드 컴포넌트 디자인 수정

닉네임이 20자고, 제목도 최대 20자인데 생각보다 이렇게 되니까 짧을 때와 길 때 디자인이 통일되지 않는 느낌이 들었습니다. 그래서 두 부분이 한 줄로 다 들어올 수 있는 길이로 설정을 해두었습니다. 반응형하면 난해해질거 같아 일단은 고정했습니다. (컴퓨터 쓰세요! 다들!! 푸하하)

스크린샷 2024-11-27 오후 3 14 05
  • 최대 길이로 입력했을 때 모습입니다.

모달창 개선

  • esc를 누를 때 모달창이 뒤로 가면서 다른 요소와 겹쳐지는 문제가 있었다.
    • 해당 문제를 해결하고자 esc를 누르면 모달창이 닫히도록 변경했다.
  • 모달창 사용방법을 변경했다.

모달창 사용 방법

  • 모달 컴포넌트를 불러오는 곳에서 const modal = useModal()을 통해 모달 인스턴스를 생성한다.
  • 그리고 해당 모달 컴포넌트에 modal={modal}로 모달을 전달해준다.
  • [모달을 사용할 때는 필요없지만, 동작 설명] 그럼 해당 모달 컴포넌트는 위 modal 인스턴스에서 받아온 ref, isOpen, openModal, closeModal을 사용할 수 있다. (*이때 각 모달은 각각의 modal 인스턴스를 생성하기 때문에 각 모달들은 독립적으로 동작하게 된다.)
    참고로 모달의 타입은 다음과 같이 정의하면 된다.
interface UseModalReturn {
  dialogRef: React.RefObject<HTMLDialogElement>;
  isOpen: boolean;
  openModal: () => void;
  closeModal: () => void;
}

폴더 구조 리팩토링 방향

MyPage만 리팩토링을 했습니다.
다음 주 리팩토링 때 어떻게 폴더 구조를 나눠야할지 고민이 있었는데, 일단 아직 개발이 다 안된 상태임에도 관련있는 파일들이 모여있지 않거나 폴더 구조가 많이 깊어질거 같고 특히나 hooks 같은 곳에도 많이 생기다 보니 분리를 하면 좋을거 같다 생각했습니다.

그래서 일단 마이페이지만 기준으로 시도해보았습니다.
container - presenter 패턴을 생각해서,

Container: UI 컴포넌트로 props 전달

  • 비즈니스 로직 처리(데이터 fetching, 상태 관리)
  • API 호출
  • 이벤트 핸들러
  • 라우팅 로직

Presenter: UI만 담당

  • 순수하게 UI 렌더링만 담당
  • props로 받은 데이터 표시
  • 스타일링
  • 레이아웃 구성
  • 조건부 렌더링

pages/MyPage 폴더에 index.tsx는 진입점이고, 여기서 import로 UI를 가져옵니다. UI 파일 이름은 직관적으로 페이지 이름 + View로 작명했습니다.

/pages
  /MyPage
    index.tsx
    MyPageView.tsx

추가적으로 사이드바 레이아웃이 간편하지만, 이 자체에 className을 전달하게 되면, 사이드바 레이아웃 안에 div 태그를 한 번 더 추가하는 일 없이 props로 클래스를 전달해서 간단하게 만들 수 있을거 같아서 사이드바 안의 children을 div로 감싸고, className을 전달할 수 있게 했습니다.

const MyPageView = ({ nickname }: MyPageViewProps) => {
  return (
    <SidebarPageLayout childrenClassName="flex flex-col flex-shrink-0 w-7xl px-12 pt-20 pb-8 overflow-auto no-scrollbar">
      <PageTitle title="마이페이지" />
      <div className="flex flex-col gap-8 w-5xl px-8 pt-8 pb-6 bg-white shadow-8 rounded-custom-l">
        <ProfileEditModal />
        <Profile nickname={nickname} />
        <QuestionSection />
      </div>
    </SidebarPageLayout >
  );
};

이렇게 전달하도록 했고 이렇게 적용하기 전에는 아래와 같았습니다.

const MyPageView = ({ nickname }: MyPageViewProps) => {
  return (
    <SidebarPageLayout>
      <div className="flex flex-col flex-shrink-0 w-7xl px-12 pt-20 pb-8 overflow-auto no-scrollbar">
        <PageTitle title="마이페이지" />
        <div className="flex flex-col gap-8 w-5xl px-8 pt-8 pb-6 bg-white shadow-8 rounded-custom-l">
          <ProfileEditModal />
          <Profile nickname={nickname} />
          <QuestionSection />
        </div>
      </div>
    </SidebarPageLayout >
  );
};
  • 뷰 파일을 구성할 때 정확히 구역을 나누기는 어려웠지만, 한 눈에 봤을 때 어떤 컴포넌트로 구성되는가?를 볼 수 있는 정도로 분리하면 좋을거 같다 생각했습니다.
  • 현재 마이페이지 뷰 파일은 사이드바 레이아웃 안에 마이페이지 타이틀과 아래 div 두개로 크게 나눠집니다. div가 pageContent 내용이고, 그 안에는 모달 + 프로필 영역 + 질문지를 볼 수 있는 영역으로 크게 나누게 됩니다. div 자체도 컴포넌트로 분리해서, 페이지 컨텐츠 내용이 들어가게 하는게 좋을거 같다 느꼈으나 아직 분리하지 않았습니다.
    • 아직 분리하지 않은 이유는 크게 코드가 긴 것도 아니고 본격적 리팩토링 단계가 아니라 나중에 고려해보고 시도하는게 낫겠다 판단했기 때문입니다!

두번째로 생각한 부분은 컴포넌트 폴더의 아주 큰 컴포넌트는 pages 폴더로 옮기는게 어떨까? 하는 생각이 들었습니다. 위 예시에서 마이페이지 뷰를 보면 해당 페이지를 구성하는 건 사이드바를 제외하면 프로필 영역과 질문 영역 두가지입니다. 이 부분은 pages에 넣고 이 부분을 구성하는 컴포넌트를 components 폴더에 넣으면 해당 폴더가 조금 더 가벼워질거 같고, 계층이 명확해질거 같다는 생각이 들었습니다.

/pages
  /MyPage
    index.tsx
    /views
      index.tsx // view 최상위 컴포넌트
      Profile.tsx
      ProfileEditModal.tsx
      QuestionSection.tsx
  • 이렇게 뷰 폴더 안에는 뷰 컴포넌트를 구성하는 세 컴포넌트를 넣어줬습니다.
  • 그리고 components/mypage는 이름을 MyPage로 변경해 MyPage 컴포넌트의 view 폴더에서 사용하는 컴포넌트를 담는다는 의미를 명확히 알 수 있게 수정했습니다.

components/MyPage의 하위는 다음과 같습니다.

/components
  /MyPage
    ButtonSection.tsx
    ProfileIcon.tsx
    /CategoryTab
    /QuesitonList
  • 이렇게 MyPage 폴더 아래에 4개의 컴포넌트가 있습니다.
  • 이때 폴더인 컴포넌트는 해당 컴포넌트 자체가 길어서 분리되어 안에 또 다른 컴포넌트가 있는 경우 명확히 구별하고자, 폴더로 만들었습니다.

정리하면, pages에서는 비즈니스 로직과 UI를 분리하고 UI를 담당하는 컴포넌트는 view에 넣어줍니다. 그리고 view를 구성하는 큰 컴포넌트(예를 들어 header, body, bottom: 예시 이름)도 넣어주고, 해당 큰 컴포넌트를 구성하는 하위 컴포넌트부터를 components의 pages 컴포넌트 이름에 해당하는(components/MyPage) 곳에 위치하게 했습니다.

  • 추가적으로 아직 마이페이지에서 연결할만한 api가 다른 페이지에서 공통으로 쓰이는 거라 api 폴더를 따로생성하거나 그러지 않았습니다. 이 부분은 나중에 다른 페이지 리팩토링할 때 특정 페이지에서만 쓰이는 훅이 있다거나 그런게 있다면 해당 관련 폴더에 모아두는 방향이 좋을거 같으나, 아직 와닿지는 않아서 따로 폴더를 생성해두지는 않았습니다.

리액트 쿼리 사용하는 부분 훅으로 만들기

리액트 쿼리로 서버의 데이터를 받아올 때 아마 페이지네이션하는 질문지 리스트라거나 이런 부분은 잦은 요청이 생길거 같습니다. 이런 부분이나 삭제하는 부분도 리액트 쿼리 사용을 결정했습니다. 삭제는 한 번만 하는데 왜?라고 생각할 수 있는데 페이지네이션 된 상태에서 예를들어 한 페이지의 아이템이 삭제되면 리액트 쿼리는 해당 페이지 아이템을 캐싱해둔 것을 무효화하고 새로 페이지의 데이터를 받아와 자동으로 UI를 업데이트 시켜줍니다. 이런 측면에서 이런 작업들도 리액트 쿼리를 사용하는게 좋겠다 판단했습니다.

  • 추가적으로 서버에서 데이터를 받아오는데 하위 컴포넌트에서 사용할 일이 많아질거 같았습니다. 컴포넌트를 분리를 시도하다보면 분명히 props drilling이 발생할텐데 이럴 경우 하위에서도 리액트 쿼리 훅을 가져와 사용하면 좋을거 같습니다.
  • 훅으로 만들지 않고 사용해도 똑같이 동작하나 코드 중복으로 인해 복잡해져서 훅을 통해 더 깔끔하게 데이터를 가져올 수 있습니다. 특히나 리액트 쿼리를 사용하게 되면 캐싱이 되기 때문에 똑같은 요청을 서버로 하지 않아서 성능상 무리가 없다 판단했습니다. 또한 훅으로 분리해서 리액트 쿼리를 필요한 곳에서 사용할 때 발생하는 중복 코드 문제도 해결이 가능해졌습니다.

💬 다음 작업 또는 논의 사항

  • 내가 만든 질문지, 내가 저장한 질문지 따로 불러오도록 연결하기
  • 회원 정보 수정 구현 -> api 완성되는대로 시작
  • 질문지 수정 페이지로 이동시키기 -> 해당 페이지가 없어서 개발해야해서 시간이 좀 걸릴 거 같음

- container-presenter 패턴 적용
- ui 컴포넌트 pages/view에서 관리
- view의 레이아웃 컴포넌트만 해당 컴포넌트에서 관리하고, 그 외 하위 컴포넌트는 components/MyPage에서 관리하도록 수정
- esc 누르면 이상해지는 동작 수정 및 esc 누르면 모달 닫히도록 수정
- 모달 상태를 다 공유하는 문제 해결: 모달 인스턴스 생성해서 사용하도록 수정
- 명세서 보고 작성하고, 백엔드 삭제 코드는 없어서 테스트 불가
@yiseungyun yiseungyun added the 🎁 Refactoring 리팩토링 label Nov 28, 2024
@yiseungyun yiseungyun self-assigned this Nov 28, 2024
Copy link
Member

@ShipFriend0516 ShipFriend0516 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

고생하셨습니다~!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

폴더구조 테스트했을 때 어떤 느낌이셨나요? 제가 구현하지 않아서 그럴 수 있지만 index로 되어있는 파일들이 많아서 직관적이지 않고 헷갈리는 느낌이 강한 것 같습니다.
저는 IDE에서 제공하는 파일 검색 기능을 많이 쓰는데요 VSC도 컨트롤 + T 누르면 파일이름으로 바로 이동할 수 있어서 자주 사용했습니다. index로 하면 검색이 안되는게 좀 불편한 것 같습니다..

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

제 생각에는 폴더이름으로 세분화하는 것은 좋은 방식인 것 같습니다. 하지만 index는 파일이 여러개 열려있을 때 헷갈리는 것 때문에 불편할 것 같은데 승윤님이 생각하는 index만의 강점이 있는지 궁금합니다

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저는 일단 index.tsx로 표기했을 때 장점이 해당 컴포넌트의 진입점인걸 알 수 있어서 좋았던거 같아요. index.tsx 컴포넌트를 이루는 하위 컴포넌트는 해당 폴더 내에서 다른 이름으로 저장되니까 다른 이름으로 저장된 애들은 index 컴포넌트 안의 구성요소라는걸 구분해준다 생각했습니다. 컴포넌트를 사용할 때는 그냥 폴더 이름으로 import가 된다는 점도 장점이라 생각했습니다! 저는 파일 검색 기능을 사용하지 않아서 그런 점은 고려를 하지 못했네요. ㅠㅠ

Copy link
Member

@ShipFriend0516 ShipFriend0516 Nov 28, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yiseungyun 저번 댓글에서 달아주신거 확인을 못하고 또 물어봤네요 허허 죄송합니다

저도 장점은 폴더의 주인? 느낌으로 쓸 수 있어서 좋은 것 같습니다. 일단 다음에 새로 만드는 컴포넌트나 페이지에 대해서는 이방법을 적용해보는 걸로 하겠습니당

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

모달 개선과 마이페이지 바뀐 UI도 좋은 것 같습니다!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🎁 Refactoring 리팩토링

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants