Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FE] feat: 사용자 프로필 정보 및 프로필 탭 구현 #1035

Merged
merged 20 commits into from
Jan 7, 2025

Conversation

chysis
Copy link
Contributor

@chysis chysis commented Dec 28, 2024


🚀 어떤 기능을 구현했나요 ?

  • 로그인 시 Topbar 우측에 뜰 사용자 프로필 정보 컴포넌트를 구현했습니다.
  • 프로필 정보 클릭 시 뜨는 프로필 탭을 구현했습니다.

🔥 어떻게 해결했나요 ?

프로필 정보

  • 768px 이하에서는 프로필 사진만 보입니다.
  • 사용자의 프로필 사진, 프로필 ID를 받아서 보여줍니다.
    • 프로필 사진 src가 없이 오는 경우를 대비해서 기본적으로는 회색으로 보이게끔 했습니다.

프로필 탭

  • 768px 이하에서는 탭에 사용자의 ID가 보입니다.

  • Dropdown 컴포넌트의 재사용을 고려했으나 다음과 같은 이유로 별도의 컴포넌트로 구현했습니다.

    1. 드롭다운에 추가적인 스타일링이 필요
    2. 기존 드롭다운은 단순 텍스트만 올 수 있는데, 프로필 탭에서는 클릭 불가능한 요소, 클릭 가능한 요소, 구분선 등이 올 수 있기 때문에 이 모든 것을 유연하게 처리하기 위해서는 새로운 컴포넌트를 만드는 것이 유리 (결정적인 이유)
  • 탭에 올 수 있는 각 요소들의 특성을 elementType으로 구분해서 적절하게 처리하고, 변화에 유연하게 대처할 수 있게 했습니다.

    • readonly: 클릭 불가능한 요소입니다. 기본 커서가 적용되고, hover 효과 없으며 클릭 불가능합니다.
    • action: 클릭 가능한 요소입니다. pointer 커서가 적용되고, hover 효과가 있으며 클릭 이벤트를 등록할 수 있습니다.
    • divider: 프로필 탭 전용 구분선입니다.
  • 모바일에서만 보여지는 요소인지를 isDisplayedOnlyMobile 속성으로 구분합니다.

image
▲ 데스크탑

image
▲ 모바일(768px 이하)

image
▲ 호버 시

  • useProfileTabElements를 통해 프로필 탭에 들어가는 메뉴들과 클릭 이벤트 등을 관리합니다. 현재는 콘솔에 표시만 하고 있지만 추후에 각 페이지의 URL이 결정되면 동작을 수정할 계획입니다.

📝 어떤 부분에 집중해서 리뷰해야 할까요?

  • 사용자의 ID가 길어질 경우 별다른 처리를 하고 있지 않아요. 768px 이상에서는 로고와 겹칠 정도로 ID가 길 수 없다고 판단했는데, 혹시 다른 의견 있다면 남겨주세요.

📚 참고 자료, 할 말

Comment on lines 80 to 81
dropdown: 997,
profileTab: 998,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

프로필 탭은 모달 이외의 다른 모든 요소보다는 위에 떠야 하기 때문에 z-index 값을 조정했습니다.

Copy link
Contributor

Choose a reason for hiding this comment

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

zIndex는 같은 부모 요소안에서 비교되는 거라, 프로필 탭 부모 요소가 ProfileTab < Topbar 순이라, Topbar 자체의 zIndex를 높여야 할 것 같아요.

  • Main zIndex를 999로 하고, 리뷰 url 생성폼 부분 배경색을 준 경우 프로필 탭이 열렸지만 Main에 가려짐
    image

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ProfileTab이 아닌 Topbar에 z-index를 설정했습니다!


export type ProfileTabElementType = 'readonly' | 'action' | 'divider';

export interface ProfileTabElement {
Copy link
Contributor

Choose a reason for hiding this comment

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

주석 👍

Comment on lines 37 to 41
content: socialType === 'github' && (
<div style={{ display: 'flex', gap: '1rem' }}>
<img src={GitHubIcon} alt="소셜 아이콘" />
<span>GitHub 계정</span>
</div>
Copy link
Contributor

@BadaHertz52 BadaHertz52 Jan 1, 2025

Choose a reason for hiding this comment

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

content 부분의 레이아웃이 반복되고, 태그나 속성을 사용할 때 VS code 편의 기능이나, eslint, prettier 도움을 받지 못할 것 같아 불편할 것 같아요.

제안 1. content 를 리터럴 객체로 변경

content :{
icon : { src:GithubIcon, alt: '소셜 아이콘'},
text : 'Github 계정'}

제안2. 프로필 탭 요소 컴포넌트 재사용

프로필 탭 안의 요소들의 레이아웃이 동일하고 ,아이콘, 글자만 달라지니 컴포넌트를 만들어서 사용하는 방법
(클릭 이벤트는 이미 있어서 제외했지만, 클릭 이벤트를 props로 넣어줘도 괜찮겠네요)

const ContentLayout: = ({ icon, text }:ContnetLayoutProps) => (
  <div style={{ display: 'flex', gap: '1rem' }}>
    <img src={icon.src} alt={icon.alt} />
    <span>{text}</span>
  </div>
);

// 객체 배열
const profileTabElements= [
  {
    elementType: 'readonly',
    isDisplayedOnlyMobile: true,
    content: <ContentLayout icon={icon1} text={profileId} />,
  },
  {
    elementType: 'editable',
    isDisplayedOnlyMobile: false,
    content: <ContentLayout iconSrc={icon2} text="다른 텍스트" />,
  },
];

Copy link
Contributor

Choose a reason for hiding this comment

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

저는 합성 컴포넌트 스타일을 사용해서 TabItem에서 어떤 요소가 렌더링되는지 직관적으로 확인할 수 있는 방식을 제안해봅니다~
(사실 OptionSwitch를 리팩토링으로 생각해뒀던 방식입니다)

렌더링 요소가 실시간으로 변하거나 관리할 아이템들이 많아지면 배열이 좋겠지만 지금은 정적인 편이고 프로필 탭인 만큼 훗날 많은 요소가 추가될 것 같지는 않다는 생각입니다.

ProfileTab은 children을 렌더하는 컴포넌트로 사용하고, 각 아이템 요소들을 별도의 컴포넌트로 분리해서 최종적으로 아래와 같이 사용해보면 좋을 것 같아요.

<ProfileTab>
      <ReadonlyItem>
        <img src={src} alt="GitHub 로고" />
        <span>GitHub 계정</span>
      </ReadonlyItem>
       ....

컴포넌트를 분리한다면 바다가 제안해준대로 Content까지 분리해서 재활용하면 좋을 것 같네요!

</div>
),
},
{
Copy link
Contributor

Choose a reason for hiding this comment

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

element에 id가 추가되면 좋을 것 같아요.
ProfileTab에서 key로 사용하면 좋고, profileTabElements 속 요소가 프로필 탭에서 어떤 부분인지 더 쉽게 눈에 띄었으면 좋겠어요

Copy link
Contributor

@BadaHertz52 BadaHertz52 left a comment

Choose a reason for hiding this comment

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

에프이 프로필 관련 구현 잘 봤어요.
변수 이름에 깔끔해서 코드 읽기 쉬웠어요.

ProfileElement,zIndex 관련해 코멘트 남겨놨으니 확인 부탁해요.

새해복 많이 받아요!!

Comment on lines 80 to 81
dropdown: 997,
profileTab: 998,
Copy link
Contributor

Choose a reason for hiding this comment

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

zIndex는 같은 부모 요소안에서 비교되는 거라, 프로필 탭 부모 요소가 ProfileTab < Topbar 순이라, Topbar 자체의 zIndex를 높여야 할 것 같아요.

  • Main zIndex를 999로 하고, 리뷰 url 생성폼 부분 배경색을 준 경우 프로필 탭이 열렸지만 Main에 가려짐
    image

Copy link
Contributor

@ImxYJL ImxYJL left a comment

Choose a reason for hiding this comment

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

칼 같은 컨벤션으로 작성된 코드 잘 봤습니다~
건의사항을 남기긴 했지만ㅎㅎ 기존 코드와의 통일성을 지킨 코드 스타일이라는 게 느껴졌어요.

확실히 기존 드롭다운을 재사용하거나 확장하는 방법보다는 새로운 컴포넌트를 만들어 처리하는 게 더 나아 보입니다.
기존 컴포넌트를 재활용하거나 상속받는 스타일로 가려면 드롭다운과 프로필 탭을 아우르는, 더 추상화된 컴포넌트를 넣는 작업 등등을 해야 할 것 같은데 품이 많이 커질 것 같네요 😅
작업량을 떠나서 의미상으로도 괜찮아 보이고요!


그리고 github username으로 최대 39글자가 올 수 있다고 하는데, 37글자 영문으로 테스트해보니 800 이하 사이즈에서 프로필이 눌리고 있어요.
image

그리고 모바일 탭에서 id가 길어질 때 줄넘김 등으로 처리해야 할 것 같아요.

image

별개로 디자인 이야기지만, 상호작용 가능한 아이템과 그렇지 않은 아이템이 시각적으로 더 명확하게 구분되면 좋겠다는 생각이 들었어요.
저라면 처음에 Github 계정이나 아이디를 한번 클릭해볼 것 같아요. (비록 마우스를 갖다댔을 때 readonly 스타일로 작동하지만요)

꽤 까다로운 작업이었을 것 같은데 고생많았어요!

@@ -0,0 +1,10 @@
export type SocialType = 'github';
Copy link
Contributor

Choose a reason for hiding this comment

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

로그인 버튼에서도 소셜 정보가 필요해서 아예 공통 타입으로 빼면 좋을 것 같아요! 로그인 도입 이후 다시 얘기해봐용

$isDisplayedOnlyMobile: boolean;
}

export const ProfileTabContainer = styled.div`
Copy link
Contributor

Choose a reason for hiding this comment

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

탭 영역도 하나의 독립적 영역인 것 같아서 최상위 컨테이너는 section으로 하면 좋을 것 같아요!

return (
<S.ProfileTabContainer>
<UndraggableWrapper>
{items.map((item, index) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

이 map 함수를 별도의 함수로 분리(예: renderTabItem)해서 ProfileTab 컴포넌트의 리턴문을 간단하게 하는 건 어떤가요?

const ProfileTab = ({ items }: ProfileTabProps) => (
  // switch 조건을 처리하는 render 함수는 별도로 선언
  <S.ProfileTabContainer>
    <UndraggableWrapper>
      {items.map((item, index) => renderProfileTabItem(item, index))} // 함수를 사용해서 main 리턴문은 한 줄로 처리
    </UndraggableWrapper>
  </S.ProfileTabContainer>
);

switch문 내부가 복잡하지 않아서 지금 방식도 괜찮다고 생각하지만 에프이의 의견이 궁금해서요!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

올리가 제안한 것처럼 return 문 내부는 짧게 만들고, render 함수를 따로 두는 편이 나을 것 같아요.

isDisplayedOnlyMobile: false,
content: (
<div style={{ display: 'flex', gap: '1rem' }}>
<img src={MenuIcon} alt="메뉴 아이콘" />
Copy link
Contributor

Choose a reason for hiding this comment

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

프로필 사진 UI는 관련 label 없이 사진만 있어서 alt 속성이 필수라고 생각했지만, 메뉴 아이콘들은 조금 고민이 되네요...
스크린 리더 사용자에게 시각 사용자와 같은 경험을 주기 위해서는 alt를 적극 활용해야 하지만 더 자세한 정보를 label로 읽을 수 있는데 아이콘의 의미를 설명하느라 중복 정보를 안내하는 게 아닌가 싶기도 해서요.
다른 팀원들의 생각도 궁금합니다~~

Copy link
Contributor

@BadaHertz52 BadaHertz52 Jan 4, 2025

Choose a reason for hiding this comment

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

저는 장식용 아이콘의 경우, 스크린 리더기로 읽어주는 게 불필요하다고 생각해서 빈문자열을 선호합니다.(웹 접근성 미션 시, 스크린 리더기 사용 시, 불필요한 데이터까지 읽어주면 피로도가 올라가더라구요.)
올리 말대로 label로 읽을 수 있기애 빈문자열로 하는 편이 더 좋을 것 같아요.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

옆에 텍스트가 함께 있기 때문에 아이콘 자체에는 큰 의미가 없을 것 같네요. 빈 문자열로 지정하는 것이 나을 듯해요 👍

Copy link
Contributor

Choose a reason for hiding this comment

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

메뉴 아이콘이 없어도 리뷰 링크 관리라는 텍스트가 이미 제공되고 있어서 탭 정보를 전달하는 데는 전혀 문제가 없다고 생각해요~ 오히려 너무 많은 정보를 전달하면 스크린 리더기 사용자 입장에서 더 불편할 수도 있을 것 같아요.. alt 속성을 빈 문자열로 처리하는 것에 한 표 하겠습니다~

장식 이미지 alt 속성 처리

Comment on lines 37 to 41
content: socialType === 'github' && (
<div style={{ display: 'flex', gap: '1rem' }}>
<img src={GitHubIcon} alt="소셜 아이콘" />
<span>GitHub 계정</span>
</div>
Copy link
Contributor

Choose a reason for hiding this comment

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

저는 합성 컴포넌트 스타일을 사용해서 TabItem에서 어떤 요소가 렌더링되는지 직관적으로 확인할 수 있는 방식을 제안해봅니다~
(사실 OptionSwitch를 리팩토링으로 생각해뒀던 방식입니다)

렌더링 요소가 실시간으로 변하거나 관리할 아이템들이 많아지면 배열이 좋겠지만 지금은 정적인 편이고 프로필 탭인 만큼 훗날 많은 요소가 추가될 것 같지는 않다는 생각입니다.

ProfileTab은 children을 렌더하는 컴포넌트로 사용하고, 각 아이템 요소들을 별도의 컴포넌트로 분리해서 최종적으로 아래와 같이 사용해보면 좋을 것 같아요.

<ProfileTab>
      <ReadonlyItem>
        <img src={src} alt="GitHub 로고" />
        <span>GitHub 계정</span>
      </ReadonlyItem>
       ....

컴포넌트를 분리한다면 바다가 제안해준대로 Content까지 분리해서 재활용하면 좋을 것 같네요!

Copy link
Contributor

@soosoo22 soosoo22 left a comment

Choose a reason for hiding this comment

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

에프이! 멋진 프로필 탭이 만들어졌네요!! 고생 많았어요!
추가로 올리가 코멘트 달아주긴 했는데! 저도 남겨볼게요!
프로필 탭이 아이디 길이에 따라 크기가 바뀌는 것 보다는 고정된 width를 주고, 아이디가 그 width를 넘어갈 때 깃허브처럼 ...으로 말줄임 처리를 하는 건 어떨까요?🤔

스크린샷 2025-01-05 오후 11 31 21

스크린샷 2025-01-05 오후 11 57 11

Comment on lines 22 to 33
export const ProfileImageWrapper = styled.div`
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;

width: 4rem;
height: 4rem;

background-color: ${({ theme }) => theme.colors.gray};
border-radius: 2rem;
`;
Copy link
Contributor

@soosoo22 soosoo22 Jan 5, 2025

Choose a reason for hiding this comment

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

제가 리뷰 링크 관리 페이지 작업하면서 겸사겸사 헤더에 있는 리뷰미 로고 글자 크기를 2.8rem으로 줄였어요! 지금 이미지 사이즈가 로고 사이즈보다 커서 이미지 사이즈도 그에 맞게 줄여야 할 것 같아요!
스크린샷 2025-01-05 오후 12 12 07

데스크탑 - width: 2.8rem
모바일 - width: 2.6rem

export const ProfileImageWrapper = styled.div`
 // ...
  width: 2.8rem;
  height: 2.8rem;

  ${media.xSmall} {
    width: 2.6rem;
    height: 2.6rem;
  }
`;

+추가로 border-radius: 50%로 줘도 둥글게 깎여요!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

바뀐 크기 공유해주어서 고마워요 !! 작업할 때 함께 바꾸겠습니다

transform: ${({ $isOpened }) => ($isOpened ? 'rotate(180deg)' : 'rotate(0deg)')};
width: 2rem;
height: 2rem;
transition: transform 0.3s ease-in-out;
Copy link
Contributor

Choose a reason for hiding this comment

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

센스 굳👍👍👍

isDisplayedOnlyMobile: false,
content: (
<div style={{ display: 'flex', gap: '1rem' }}>
<img src={MenuIcon} alt="메뉴 아이콘" />
Copy link
Contributor

Choose a reason for hiding this comment

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

메뉴 아이콘이 없어도 리뷰 링크 관리라는 텍스트가 이미 제공되고 있어서 탭 정보를 전달하는 데는 전혀 문제가 없다고 생각해요~ 오히려 너무 많은 정보를 전달하면 스크린 리더기 사용자 입장에서 더 불편할 수도 있을 것 같아요.. alt 속성을 빈 문자열로 처리하는 것에 한 표 하겠습니다~

장식 이미지 alt 속성 처리

@chysis
Copy link
Contributor Author

chysis commented Jan 6, 2025

수정 사항

프로필 탭의 최대 width 설정 (25rem)

  • 모바일 환경 고려하여 최대 width를 결정했습니다.
  • 아예 고정 width를 가지기보다는 기본적으로 max-content를 따르되 그 최댓값을 가지도록 했습니다.
    image
    image

리팩토링

  • ReadonlyItem, ActionItem, Divider를 별도의 컴포넌트로 분리했습니다.
  • element 배열에서 content 부분을 객체 리터럴로 받게 됩니다. tsx 코드를 직접 넣지 않아도 됩니다.
    • 다양한 형태의 element가 올 수 있다 생각하고 열어두었는데, 과한 고민인 것 같네요. icon+text 조합 정도면 충분할 것 같습니다.
  • ProfileTab에서 element를 렌더링하는 부분을 함수로 분리했습니다.'

z-index 수정

  • ProfileTab이 아닌 Topbar에 z-index를 설정했습니다. modal보다는 낮게 설정했습니다.

readonly 타입의 element의 경우, 읽기 전용임을 구분할 수 있도록 했으면 좋겠다는 의견이 있었어요. 당장은 뾰족한 수가 생각나지 않아서 다음 회의에 다같이 이야기해봐도 좋을 것 같아요.

Copy link
Contributor

@ImxYJL ImxYJL left a comment

Choose a reason for hiding this comment

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

변경사항 확인했어요. 저도 고정 width 말고 max-width를 주는 방식이 나은 것 같아요~

코드를 보다가 문득 useProfileTabElementsProfileInfo에서 호출해서 props로 ProfileTab에 넘겨주는 방식을 선택한 이유가 있는지 궁금해졌어요.
useProfileTabElements가 사실상 탭 컴포넌트에서만 쓰는, 역할 분리만을 위한 훅이라면 그냥 탭 내부에서 호출하는 게 책임 분리 측면에서 낫지 않을까 싶었습니다!
그렇게 중요한 부분은 아니라고 생각해서 어프루브는 지금 할게용 고생했어요!

@chysis
Copy link
Contributor Author

chysis commented Jan 6, 2025

useProfileTabElements를 ProfileInfo에서 호출해서 props로 ProfileTab에 넘겨주는 방식을 선택한 이유가 있는지

useProfileTabElements 훅은 소셜 서비스와 유저 아이디 정보를 필요로 해요. ProfileInfo가 이 두 정보를 props로 받고 있는데, ProfileTab으로 한 번 더 props 전달하기보다는 ProfileTab이 elements 정보를 받게끔 했어요.

지금 다시 보니 ProfileInfo와 해당 훅은 연관이 없어서 ProfileTab에서 호출하는 방식이 더 좋을 것 같네요! 수정하겠습니다 :)

Copy link
Contributor

@soosoo22 soosoo22 left a comment

Choose a reason for hiding this comment

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

에프이~ 수정사항 확인 완료했습니다! 수고했어요👏

min-width: 100%;
max-width: 25rem;
Copy link
Contributor

Choose a reason for hiding this comment

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

오오! 고정된 width가 아닌 max-width를 주는 게 훨씬 좋은 것 같아요👍👍

Copy link
Contributor

@BadaHertz52 BadaHertz52 left a comment

Choose a reason for hiding this comment

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

수정 사항 확인했습니다. 🚀

@BadaHertz52 BadaHertz52 merged commit 8107b1d into develop Jan 7, 2025
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

[FE] 로그인 시 보일 사용자 프로필과 프로필 탭을 구현한다.
4 participants