[7주차] Ditda 권오진 & 박유민 과제 제출합니다.#2
Conversation
[SETTING] 디자인 시스템 정의
[FEAT] 홈&온보딩 공통 컴포넌트 구현 및 레이아웃 설정
…e-members [FEAT] 투표 및 멤버 페이지 UI 수정 및 리팩토링
[FEAT] Auth API 연동 및 인증 구조 구현
| export const ID_REGEX = /^[a-zA-Z0-9]{6,20}$/; | ||
| export const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; | ||
| export const PASSWORD_REGEX = | ||
| /^(?=.*[A-Za-z])(?=.*\d)(?=.*[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]).{8,16}$/; |
There was a problem hiding this comment.
회원가입 정보 입력할 때 유효성을 검사하는 데 사용하는 데 정규식을 활용하고 상수로 정의하는 방식이 좋았습니다!
| <Button | ||
| key={member.name} | ||
| isSelected={displayMember === member.name} | ||
| onClick={() => { | ||
| if (hasVoted) return; | ||
| setSelectedMember(member.name); | ||
| }} | ||
| > |
There was a problem hiding this comment.
개인적으로 무언가를 선택할 때 아무것도 선택된 것이 없는 빈 상태를 선호하는 편이라서 저와 같은 사용자를 고려하여, 이미 선택한 프로젝트 버튼을 한 번 더 클릭해서 선택을 취소할 수 있게 되면 좋을 것 같아요!
yiyoonseo
left a comment
There was a problem hiding this comment.
수고하셨습니다! 바쁘신 와중에도 디자인과 코드가 늘 깔끔해서 놀랍네여 ㅎㅎ 짱입니당 . .
| } finally { | ||
| setIsIdChecking(false); | ||
| } | ||
| }; |
| variant="check" | ||
| disabled={isCheckDisabled} | ||
| onClick={key === "id" ? handleCheckId : handleCheckEmail} | ||
| > |
| }); | ||
|
|
||
| const idValue = useWatch({ control, name: "id", defaultValue: "" }); | ||
| const emailValue = useWatch({ control, name: "email", defaultValue: "" }); |
There was a problem hiding this comment.
한글자씩 타이핑 할 때마다 리렌더링하게 되는 것 같아요!
fieldState 기능을 활용해봐도 좋을 것 같습니당
| export const FIELDS: { key: string; label: ReactNode; placeholder: string; type?: string }[] = [ | ||
| { key: "id", label: "아이디 *", placeholder: "6~20자 내로 입력해주세요" }, | ||
| { key: "email", label: "이메일 *", placeholder: "이메일을 입력해주세요", type: "email" }, | ||
| { | ||
| key: "password", | ||
| label: "비밀번호 *", | ||
| placeholder: "비밀번호를 입력해주세요", | ||
| type: "password", | ||
| }, | ||
| { | ||
| key: "passwordConfirm", | ||
| label: ( | ||
| <> | ||
| 비밀번호 <br /> 재입력 * | ||
| </> | ||
| ), | ||
| placeholder: "비밀번호를 재입력해주세요", | ||
| type: "password", | ||
| }, | ||
| ]; |
There was a problem hiding this comment.
email, password, passwordConfirm 필드에 각각 type="email", type="password" 를 지정해 브라우저가 입력 필드의 의미를 잘 인식할 수 있도록 한 점이 좋았습니다. 덕분에 앱 실행 시, 이메일 자동완성이나 비밀번호 매니저 연동이 자연스럽게 동작하는 것을 확인할 수 있어서 UX 측면에서 좋아 보입니다!
minseo0614
left a comment
There was a problem hiding this comment.
7주차 과제 수고하셨습니다!! 코드 리뷰하면서 많이 배울 수 있었습니당
| import { postCheckDuplicateEmail, postCheckDuplicateId, postSignUp } from "@/lib/apis/auth"; | ||
| import type { ApiResponse } from "@/types/common"; | ||
|
|
||
| type CheckStatus = "idle" | "available" | "duplicate"; |
There was a problem hiding this comment.
boolean이 아니라 union 타입으로 둔 점이 좋네요! idle, available, duplicate 상태가 명확히 구분돼서 의미가 더 잘 드러나는 것 같아요.
| <CTA | ||
| label="투표하러 가기" | ||
| onClick={() => { | ||
| if (getCookieToken()) { | ||
| router.push("/vote"); | ||
| } else { | ||
| setModalOpen(true); |
There was a problem hiding this comment.
클릭 시점에 토큰 여부를 확인해서 가드하는 방식이 좋네요! 불필요한 리다이렉트나 화면 깜빡임 없이 자연스럽게 처리되는 것 같아요.
| export const dispatchAuthChange = () => { | ||
| window.dispatchEvent(new Event(AUTH_CHANGE_EVENT)); | ||
| }; |
There was a problem hiding this comment.
CustomEvent로 인증 상태 변경을 감지하도록 처리한 점이 좋은 것 같아요!
작업 관련 링크
구현 사항
디자인 시스템
공통 컴포넌트
Header(햄버거바 → 사이드바 연동)Modal(오버레이 클릭 / ESC 키로 닫기)CTA,Button,InputField,DropDown,TabToggle,Chip,ProfileCard페이지 (전 페이지 반응형)
인증
ky인스턴스 구현 (요청 전 토큰 자동 주입, 401 시 토큰 재발급 및 요청 재시도)react-hook-form+zod스키마 기반 유효성 검사/members,/vote/*경로 접근 차단느낀 점 및 배운 점
오진
유민
md:prefix로 768px 이상을 대응했습니다. Tailwind의 반응형 프리픽스는 내부적으로 CSS 미디어 쿼리로 컴파일되는데, 별도의 미디어 쿼리 블록 없이 클래스 하나로 반응형을 제어할 수 있어 코드가 훨씬 간결해진다는 걸 체감했습니다.yarn을 사용해봤는데, 의존성 설치 속도가 빠르고yarn.lock으로 팀원 간 패키지 버전을 일관되게 유지할 수 있다는 점이 좋았습니다.axios대신ky를 선택했는데, 번들 사이즈가 훨씬 가볍고 Fetch API 기반이라 Next.js 환경과 잘 맞았습니다. 특히 요청을 보내기 전,후 시점에 원하는 동작을 끼워 넣을 수 있어서, 토큰을 자동으로 헤더에 담거나 인증이 만료됐을 때 자동으로 재발급받고 실패한 요청을 다시 보내는 로직을 깔끔하게 처리할 수 있었습니다.react-hook-form과zod를 조합해 스키마 기반 유효성 검사를 적용했습니다. zod 스키마 하나로 유효성 규칙, 에러 메시지, 타입 추론을 한 곳에서 정의할 수 있고, react-hook-form이 불필요한 리렌더링을 최소화해 성능 면에서도 이점이 있었습니다.refine()으로 비밀번호 일치 여부 같은 크로스 필드 검증도 간결하게 처리할 수 있어서, 폼이 복잡해질수록 유지보수 비용이 낮아진다는 걸 체감했습니다.document.cookie에 직접 대입하는 방식으로 구현했으나, 클라이언트에서 직접 쿠키를 조작하면 보안상 문제가 있어 Next.js 서버 액션으로 교체했습니다. 이후 메모리와 쿠키를 함께 사용하는 이중 저장 방식을 적용해, 평상시엔 메모리의 토큰을 우선 사용하고 새로고침 시 메모리가 초기화되면 쿠키에서 토큰을 복구하도록 구현했습니다. 401 응답 시에는ky의afterResponse훅에서 자동으로 refresh 토큰을 요청하고 실패한 API를 재시도하는 흐름까지 갖췄습니다. 인증 흐름 전반을 직접 설계하면서 토큰 생명주기와 보안 트레이드오프에 대해 깊이 생각해볼 수 있었습니다. (다만 현재 토큰 재발급 API에 백엔드팀 이슈가 있어 추후 보완 예정입니다!)proxy.ts를 활용해 미인증 사용자의 경로 접근을 서버 단에서 차단하는 흐름도 구현했습니다. Next.js 미들웨어는 요청이 페이지에 도달하기 전, 엣지에서 실행되기 때문에 클라이언트 렌더링 전에 인증 여부를 검사할 수 있다는 걸 알게 되었습니다.request.cookies.get("accessToken")으로 쿠키 토큰 유무를 확인하고, 없으면/로 redirect하는 방식으로 구현하고,config.matcher에/vote/:path*,/members/:path*를 지정해 보호가 필요한 경로만 선택적으로 적용했습니다. 앞서 기술한 서버 액션 기반 쿠키 저장 방식 덕분에, 미들웨어에서도HttpOnly쿠키를 그대로 읽을 수 있어 인증 흐름이 일관되게 연결됐습니다.