Conversation
빌드 결과빌드 성공 🎉 |
🎨 스토리북 배포 완료!변경된 컴포넌트의 디자인을 확인해주세요. |
| <p className={styles.descriptionContainer}> | ||
| @@님, 목표를 향해 <br /> | ||
| {dayData.progressDays}일째 달려가고 있어요 | ||
| </p> |
There was a problem hiding this comment.
p1) API 연결하면서 사용자 이름을 받아와야 하는데, 해당 부분 더미 텍스트 말고 API 명세 참고하셔서 실제 데이터와 타입을 연동하고, 목데이터라도 만들어서 동적으로 렌더링되도록 처리해주시면 좋을 것 같습니다.
There was a problem hiding this comment.
해당 부분은 로그인 로직이 구현되면, 로그인 성공 시 사용자 정보를 로컬 스토리지나 전역 상태 관리 등의 방식으로 저장할 것으로 판단해서 일단 임시로 더미 데이터를 넣어두었고, API 연동 시에 해당 구조에 맞게 렌더링 되도록 수정할 계획입니다 ..!
| if (!detailData || detailData.completedTodoCount === 0) { | ||
| return ( | ||
| <div className={styles.detailContainer({ state })}> | ||
| <p className={styles.defaultText} dangerouslySetInnerHTML={{ __html: DEFAULT_MESSAGE }} /> |
There was a problem hiding this comment.
p4) dangerouslySetInnerHTML 왜 쓰신 건지 여쭤봐도 될까요?
There was a problem hiding this comment.
아래처럼 문장 사이에 br 태그를 넣어 줄 바꿈을 하고 싶은데, 상수 처리도 하고 싶어서 찾아봤더니 dangerouslySetInnerHTML 라는 게 있길래 써봤습니닷 .. 보안 상 이유로 안쓰는 게 좋긴 하나, 사용자 입력이 없는 부분이라 괜찮다고 판단되어 넣었는데 혹시 좋은 방법 있으면 수정하겠습니다 !!
const DEFAULT_MESSAGE = '원하는 날의 점을 클릭하고 <br/> 그날 내가 한 일을 확인해보세요!';There was a problem hiding this comment.
움… dangerouslySetInnerHTML은 정말 HTML 태그가 필요할 때, 예를 들면 다음과 같은 경우에만 사용하는 것이 좋다고 합니다.
- 콘텐츠가 마크다운이나 CMS(콘텐츠 관리 시스템)처럼 HTML 형식으로 내려오는 경우
- b, i, ul, a href=... 등 복잡한 HTML 구조가 포함된 문자열을 실제 HTML로 렌더링해야 할 때
지금처럼 단순한 줄바꿈 정도만 필요한 상황이라면 JSX로 직접 <br />을 쓰는 게 더 안전하고 직관적인 방식이라고 해요!
간단한 경우에는 JSX를 쓰는 쪽으로 가는 게 유지보수에도 더 좋을 것 같습니당 🙌
상수 처리도 하고싶고 br태그도 사용하고 싶으시다면...
코드가 좀 길어지지만
const MESSAGE_PARTS = ['원하는 날의 점을 클릭하고', '그날 내가 한 일을 확인해보세요!'];
<p>
{MESSAGE_PARTS.map((text, i) => (
<span key={i}>
{text}
{i < MESSAGE_PARTS.length - 1 && <br />}
</span>
))}
</p>
이렇게 바꿀 수 있겠네요....
되도록이면 dangerouslySetInnerHTML을 사용하는걸 지양하면 좋겠다는 입장인데 다른 분들 의견도 궁금해욥
| const state = !detailData || detailData.completedTodoCount === 0 ? 'empty' : 'filled'; | ||
|
|
||
| if (!detailData) { | ||
| if (!detailData || detailData.completedTodoCount === 0) { | ||
| return ( |
There was a problem hiding this comment.
p1) 조건식이 동일하게 사용되고 있어 불필요한 중복같은데 멋이 다른건가요?
There was a problem hiding this comment.
눈이 감겨서 못봤나봐요 .. 중복되는 조건이라 수정했습니다 !!!
| const StreakGrid = ({ progressDays, onClick }: StreakGridProps) => { | ||
| const dots = Array.from({ length: TOTAL_DOTS }, (_, i) => { | ||
| const isFilled = i < progressDays; | ||
| const day = i + 1; | ||
|
|
||
| const DotIcon = isFilled ? IcStreakerDot : IcStreakerDotDefault; | ||
|
|
||
| return <DotIcon key={i} className={styles.dotIcon({ clickable: isFilled })} />; | ||
| return ( | ||
| <button onClick={isFilled ? () => onClick(day) : undefined}> | ||
| <DotIcon key={i} className={styles.dotIcon({ clickable: isFilled })} /> | ||
| </button> | ||
| ); |
There was a problem hiding this comment.
p1) key는 최상단 요소인 button에 붙여야 리액트가 각 반복 항목을 정확히 추적할 수 있습니다람쥐.
return (
<button key={i} onClick={isFilled ? () => onClick(day) : undefined}>
<DotIcon className={styles.dotIcon({ clickable: isFilled })} />
</button>
);
깔꼼스딱스
There was a problem hiding this comment.
우왓 좋은 정보 감사합니다 ! 수정했습니다아
빌드 결과빌드 성공 🎉 |
빌드 결과빌드 성공 🎉 |
빌드 결과빌드 성공 🎉 |
jisooooooooooo
left a comment
There was a problem hiding this comment.
고생하셨습니다~~~~
코멘트 하나만 확인 부탁드려요!!!😆
빌드 결과빌드 성공 🎉 |
|
✅ Storybook 배포 완료! 🔗 https://6866a01b6253382f0f391b33-avbzrvuyas.chromatic.com/ |
빌드 결과빌드 성공 🎉 |
🎨 스토리북 배포 완료!변경된 컴포넌트의 디자인을 확인해주세요. |

💡 Summary
✅ Tasks
👀 To Reviewer
졸려요
📸 Screenshot
2025-07-12.3.40.41.mov