Skip to content

[7주차] Ditda 권오진 & 박유민 과제 제출합니다.#2

Closed
waldls wants to merge 110 commits into
CEOS-Developers:masterfrom
Hi-Five-Official:master
Closed

[7주차] Ditda 권오진 & 박유민 과제 제출합니다.#2
waldls wants to merge 110 commits into
CEOS-Developers:masterfrom
Hi-Five-Official:master

Conversation

@waldls
Copy link
Copy Markdown

@waldls waldls commented May 15, 2026

작업 관련 링크


구현 사항

image

디자인 시스템

  • Ditda Color, Typography, Radius 토큰 정의

공통 컴포넌트

  • Header (햄버거바 → 사이드바 연동)
  • Modal (오버레이 클릭 / ESC 키로 닫기)
  • CTA, Button, InputField, DropDown, TabToggle, Chip, ProfileCard

페이지 (전 페이지 반응형)

  • — 배경 그래픽 애니메이션 및 반응형 구현
  • 로그인 — 로그인 UI 및 API 연동
  • 회원가입 — 회원가입 UI 및 API 연동 (아이디, 이메일 중복 확인 포함)
  • 투표 — 파트장 투표 / 데모데이 투표 페이지
  • 투표 결과 랭킹 — 파트장, 데모데이 결과 랭킹 페이지
  • 멤버 페이지
  • error, loading, not-found 페이지

인증

  • ky 인스턴스 구현 (요청 전 토큰 자동 주입, 401 시 토큰 재발급 및 요청 재시도)
  • 공통 응답 타입 및 에러 코드 정의
  • 메모리 + 쿠키 이중 토큰 저장 (새로고침 시 쿠키에서 복구)
  • 서버 액션 기반 쿠키 설정/삭제
  • react-hook-form + zod 스키마 기반 유효성 검사
  • 회원가입, 토큰 재발급, 로그아웃, 로그인, 아이디 중복 확인, 이메일 중복 확인 API 연동
  • 로그인하지 않은 사용자의 /members, /vote/* 경로 접근 차단

느낀 점 및 배운 점

오진

모바일 디자인 수정, 공통 컴포넌트 구현, 투표 및 멤버 페이지 퍼블리싱

  • 이번 과제에서 투표 관련 페이지 및 멤버 페이지 컴포넌트 및 UI를 구현했습니다. 처음에는 프론트/백엔드 파트장, 데모데이에 대한 페이지를 각각 구현하려고 했는데 구조가 동일해 동적 라우팅으로 통합하였습니다.
  • 투표 선택 및 투표 확정 모달을 구현하였습니다. 사용자가 후보를 선택하면 CTA 버튼 활성화를 시키고 해당 버튼을 누르면 투표 확정 모달을 띄우도록 하였습니다. 모달을 통해 투표가 확정되면 CTA 버튼을 숨겨 중복 투표를 방지하였습니다.
  • 현재 투표 및 멤버 페이지의 데이터를 Mock 데이터 파일을 따로 만들어서 필요한 데이터를 가져오고 세션 스토리지를 통해 투표 상태 관리를 하고 있는데 추후 수정할 예정입니다.
  • 이번 과제를 하면서 피그마 디자인 및 과정을 간단하게 경험해보았는데 정확한 값이 아닌 매번 비슷한 값을 이용하여 UI를 구현하려고 한 적이 이번 과제 중 가장 어려웠던 포인트이며 디자이너의 고충 또한 몸소 경험해볼 수 있었습니다.

유민

전체적인 디자인, 공통 컴포넌트 구현 및 레이아웃 설계, AUTH 퍼블리싱 및 API 연동

  • 디자인의 경우 하니홈을 레퍼런스로 삼되, 프로젝트 이름인 '딧다 → 잇다 → connect'의 흐름을 선적인 요소로 시각화하는 방향으로 작업했습니다. 평소에 개발에만 집중하다 보니 디자인이 이렇게 많은 고민을 요구하는 작업인지 새삼 느꼈고, 모바일까지 고려해야 해서 더욱 복잡했습니다 🥲 그래도 피그마의 컴포넌트, 오토 레이아웃, 프레임 등 다양한 기능을 실제로 활용해보는 좋은 경험이 됐습니다.
  • 반응형의 경우 모바일 우선으로 기본 스타일을 잡고, md: prefix로 768px 이상을 대응했습니다. Tailwind의 반응형 프리픽스는 내부적으로 CSS 미디어 쿼리로 컴파일되는데, 별도의 미디어 쿼리 블록 없이 클래스 하나로 반응형을 제어할 수 있어 코드가 훨씬 간결해진다는 걸 체감했습니다.
  • 이번 과제에서는 익숙한 스택 외에 처음 써보는 도구들을 직접 사용해보는 것에도 의의를 뒀습니다. 패키지 매니저로 yarn을 사용해봤는데, 의존성 설치 속도가 빠르고 yarn.lock으로 팀원 간 패키지 버전을 일관되게 유지할 수 있다는 점이 좋았습니다.
  • 데이터 패칭 라이브러리도 axios 대신 ky를 선택했는데, 번들 사이즈가 훨씬 가볍고 Fetch API 기반이라 Next.js 환경과 잘 맞았습니다. 특히 요청을 보내기 전,후 시점에 원하는 동작을 끼워 넣을 수 있어서, 토큰을 자동으로 헤더에 담거나 인증이 만료됐을 때 자동으로 재발급받고 실패한 요청을 다시 보내는 로직을 깔끔하게 처리할 수 있었습니다.
  • react-hook-formzod를 조합해 스키마 기반 유효성 검사를 적용했습니다. zod 스키마 하나로 유효성 규칙, 에러 메시지, 타입 추론을 한 곳에서 정의할 수 있고, react-hook-form이 불필요한 리렌더링을 최소화해 성능 면에서도 이점이 있었습니다. refine()으로 비밀번호 일치 여부 같은 크로스 필드 검증도 간결하게 처리할 수 있어서, 폼이 복잡해질수록 유지보수 비용이 낮아진다는 걸 체감했습니다.
  • 토큰 저장 방식에서 고민이 많았습니다. 처음에는 document.cookie에 직접 대입하는 방식으로 구현했으나, 클라이언트에서 직접 쿠키를 조작하면 보안상 문제가 있어 Next.js 서버 액션으로 교체했습니다. 이후 메모리와 쿠키를 함께 사용하는 이중 저장 방식을 적용해, 평상시엔 메모리의 토큰을 우선 사용하고 새로고침 시 메모리가 초기화되면 쿠키에서 토큰을 복구하도록 구현했습니다. 401 응답 시에는 kyafterResponse 훅에서 자동으로 refresh 토큰을 요청하고 실패한 API를 재시도하는 흐름까지 갖췄습니다. 인증 흐름 전반을 직접 설계하면서 토큰 생명주기와 보안 트레이드오프에 대해 깊이 생각해볼 수 있었습니다. (다만 현재 토큰 재발급 API에 백엔드팀 이슈가 있어 추후 보완 예정입니다!)
  • 여기에 더해, proxy.ts를 활용해 미인증 사용자의 경로 접근을 서버 단에서 차단하는 흐름도 구현했습니다. Next.js 미들웨어는 요청이 페이지에 도달하기 전, 엣지에서 실행되기 때문에 클라이언트 렌더링 전에 인증 여부를 검사할 수 있다는 걸 알게 되었습니다. request.cookies.get("accessToken")으로 쿠키 토큰 유무를 확인하고, 없으면 /로 redirect하는 방식으로 구현하고, config.matcher/vote/:path*, /members/:path*를 지정해 보호가 필요한 경로만 선택적으로 적용했습니다. 앞서 기술한 서버 액션 기반 쿠키 저장 방식 덕분에, 미들웨어에서도 HttpOnly 쿠키를 그대로 읽을 수 있어 인증 흐름이 일관되게 연결됐습니다.
  • 백엔드 팀에게 너무 감사합니다,,,🙇🏻 시간이 부족하셨을 텐데 Auth API를 모두 완성해주시고, 공통 에러 코드와 API 명세서까지 꼼꼼하게 작성해 빠르게 공유해주셔서 불편함 없이 작업할 수 있었습니다. 원활한 쿠키 사용을 위한 서버 서브 도메인 설정까지 부탁드렸는데 모두 신속하게 반영해주신 덕분에 프론트에서도 SPEC에 맞게 미리 구조를 잡고 빠르게 진행할 수 있었습니다.
  • 협업 방식 측면에서도 배운 점이 있었습니다. 디자인 목업을 완성한 직후 바로 백엔드 팀에 피그마를 공유하고 일정을 조율했는데, 개발 시작 전에 인터페이스를 먼저 맞춰두니 양쪽이 병렬로 작업할 수 있었고 API SPEC과 서버 단에서 불필요한 수정도 줄어들었습니다. 앞으로도 디자인, 개발 착수 전 스펙 합의를 먼저 하는 방식을 유지하고 싶다고 느꼈습니다.

waldls and others added 30 commits May 11, 2026 22:41
[FEAT] 홈&온보딩 공통 컴포넌트 구현 및 레이아웃 설정
KOJ50 and others added 25 commits May 15, 2026 02:37
…e-members

[FEAT] 투표 및 멤버 페이지 UI 수정 및 리팩토링
[FEAT] Auth API 연동 및 인증 구조 구현
Comment thread src/constants/regex.ts
Comment on lines +1 to +4
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}$/;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

회원가입 정보 입력할 때 유효성을 검사하는 데 사용하는 데 정규식을 활용하고 상수로 정의하는 방식이 좋았습니다!

Comment on lines +55 to +62
<Button
key={member.name}
isSelected={displayMember === member.name}
onClick={() => {
if (hasVoted) return;
setSelectedMember(member.name);
}}
>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

개인적으로 무언가를 선택할 때 아무것도 선택된 것이 없는 빈 상태를 선호하는 편이라서 저와 같은 사용자를 고려하여, 이미 선택한 프로젝트 버튼을 한 번 더 클릭해서 선택을 취소할 수 있게 되면 좋을 것 같아요!

Copy link
Copy Markdown

@yiyoonseo yiyoonseo left a comment

Choose a reason for hiding this comment

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

수고하셨습니다! 바쁘신 와중에도 디자인과 코드가 늘 깔끔해서 놀랍네여 ㅎㅎ 짱입니당 . .

Comment thread src/app/signup/page.tsx
} finally {
setIsIdChecking(false);
}
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

중복 방지 로직까지 세심하게 확인할 수 있는 점이 좋네요!

Comment thread src/app/signup/page.tsx
variant="check"
disabled={isCheckDisabled}
onClick={key === "id" ? handleCheckId : handleCheckEmail}
>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

type = 'button'을 명시해주면 더 좋을 것 같습니다!

Comment thread src/app/signup/page.tsx
});

const idValue = useWatch({ control, name: "id", defaultValue: "" });
const emailValue = useWatch({ control, name: "email", defaultValue: "" });
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

한글자씩 타이핑 할 때마다 리렌더링하게 되는 것 같아요!
fieldState 기능을 활용해봐도 좋을 것 같습니당

https://react-hook-form.com/docs/useform/getfieldstate

Copy link
Copy Markdown

@a-00-a a-00-a left a comment

Choose a reason for hiding this comment

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

안녕하세요! 7주차 과제도 수고 많으셨습니당~!

Comment thread src/constants/signup.tsx
Comment on lines +8 to +27
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",
},
];
Copy link
Copy Markdown

@a-00-a a-00-a May 18, 2026

Choose a reason for hiding this comment

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

email, password, passwordConfirm 필드에 각각 type="email", type="password" 를 지정해 브라우저가 입력 필드의 의미를 잘 인식할 수 있도록 한 점이 좋았습니다. 덕분에 앱 실행 시, 이메일 자동완성이나 비밀번호 매니저 연동이 자연스럽게 동작하는 것을 확인할 수 있어서 UX 측면에서 좋아 보입니다!

Copy link
Copy Markdown

@minseo0614 minseo0614 left a comment

Choose a reason for hiding this comment

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

7주차 과제 수고하셨습니다!! 코드 리뷰하면서 많이 배울 수 있었습니당☺️

Comment thread src/app/signup/page.tsx
import { postCheckDuplicateEmail, postCheckDuplicateId, postSignUp } from "@/lib/apis/auth";
import type { ApiResponse } from "@/types/common";

type CheckStatus = "idle" | "available" | "duplicate";
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

boolean이 아니라 union 타입으로 둔 점이 좋네요! idle, available, duplicate 상태가 명확히 구분돼서 의미가 더 잘 드러나는 것 같아요.

Comment thread src/app/page.tsx
Comment on lines +32 to +38
<CTA
label="투표하러 가기"
onClick={() => {
if (getCookieToken()) {
router.push("/vote");
} else {
setModalOpen(true);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

클릭 시점에 토큰 여부를 확인해서 가드하는 방식이 좋네요! 불필요한 리다이렉트나 화면 깜빡임 없이 자연스럽게 처리되는 것 같아요.

Comment on lines +29 to +31
export const dispatchAuthChange = () => {
window.dispatchEvent(new Event(AUTH_CHANGE_EVENT));
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

CustomEvent로 인증 상태 변경을 감지하도록 처리한 점이 좋은 것 같아요!

@waldls waldls closed this May 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants