공부하기
노잼선거 코드를 본인이 직접 설명할 수 있도록 정리한 학습 가이드. 백엔드(SpringBoot 등) 개발자가 풀스택으로 넘어가는 다리.
※ 위쪽: 10개 학습 영역 (코드 위치 + 공부 자료 + 체크 질문). 아래쪽: 면접 예상 질문 25개.
- 먼저 필수 항목 4개 (Next.js · React · Vercel · 인증)
- 그다음 중요 항목 4개 (TS · Tailwind · Upstash · SEO)
- 마지막 보너스 2개 (데이터 처리 · UX 패턴)
- 각 항목 끝 체크 질문에 본인이 답할 수 있으면 다음으로
Next.js 14 App Router
왜 필요? 노잼선거의 라우팅·렌더링 모드·메타데이터 전부 App Router 기반. Server Component vs Client Component 차이를 모르면 코드 절반 못 읽는다.
app/page.tsx— 메인 페이지 — 서버 컴포넌트app/[region]/page.tsx— 동적 라우팅 ([region])app/api/votes/route.ts— API Route — Edge runtimecomponents/RaceTabs.tsx— "use client" 클라이언트 컴포넌트components/DDayBadge.tsx— 클라이언트 컴포넌트 — 매분 갱신
- 왜 페이지에 `export const dynamic = "force-dynamic"` 을 썼나? (힌트: 마스킹 토글)
- `generateMetadata`와 정적 `metadata` 객체 차이?
- `use client`를 안 쓰면 무엇이 안 되나?
- `cookies()` 함수를 클라이언트 컴포넌트에서 쓰면? (안 됨)
React 기초 (Hooks)
왜 필요? 백엔드 SpringBoot만 했다면 React useState/useEffect가 낯설다. 노잼선거 클라이언트 컴포넌트 거의 다 hooks 기반.
components/VoteWidget.tsx— useState · useEffect · fetchcomponents/Countdown.tsx— setInterval로 매초 갱신components/RaceTabs.tsx— URL hash·query 보는 useEffectcomponents/BoardClient.tsx— 페이징 + 폼 상태
- `useState`의 setter를 호출하면 컴포넌트가 어떻게 다시 렌더되나?
- `useEffect`의 두 번째 인자(deps 배열) 의미?
- Hydration mismatch는 왜 발생? 노잼선거에서 어떻게 해결?
- 함수 컴포넌트에서 `this` 못 쓰는 이유?
TypeScript
왜 필요? Java/Spring과 비슷한 정적 타입이지만 구조적 타이핑·유니온 타입·제네릭이 더 유연. 노잼선거 전체가 TS.
data/types.ts— Candidate · Race · Region 인터페이스lib/mask.ts— Generic `maskEntry<T>` 함수lib/districtRank.ts— Union 타입 `'wealth' | 'crim'`
- `interface`와 `type` 차이? 언제 어떤 거 쓰나?
- `Candidate['partyKey']`처럼 타입에서 값 꺼내는 문법 의미?
- `as unknown as Foo` 캐스팅을 왜 쓰나? 안전한가?
- 제네릭 함수 `function f<T>(x: T): T` 의 T는 어떻게 추론되나?
Tailwind CSS
왜 필요? 노잼선거 전체 디자인이 Tailwind 유틸리티 클래스. 다크 배경·네온 그린 톤도 클래스 조합.
tailwind.config.ts— 색상 토큰 (paper, ink, neon) 정의app/globals.css— 글로벌 + CSS 변수components/Top5Teaser.tsx— 반응형 클래스 (sm:, md:)components/RaceTabs.tsx— sticky · backdrop-blur 등
- `sm:` `md:` 접두사 의미? 어떤 breakpoint?
- `tracking-tightest` 같은 커스텀 클래스는 어디 정의?
- Dark mode를 Tailwind에서 어떻게 지원? 노잼선거는?
- `@apply` 디렉티브 언제 쓰면 안 좋나?
Vercel 인프라
왜 필요? 노잼선거 호스팅 전체가 Vercel. Edge Runtime, Hobby 한도, 환경변수, OG 이미지 동적 생성까지.
app/api/votes/route.ts— `export const runtime = "edge"`app/opengraph-image.tsx— 동적 OG 이미지 (next/og).env.local— 환경변수 (ADMIN_PASSWORD, KV_*)
- Edge Runtime과 Node.js Runtime 차이? 언제 어떤 거?
- Vercel Hobby 무료 한도는? 노잼선거 사용량 대비 여유?
- 환경변수가 빌드 시점에 박히는 것과 런타임에 읽히는 것 차이?
- OG 이미지 캐시 무효화는 어떻게? (`revalidate = 3600`)
Upstash Redis (Vercel KV)
왜 필요? 투표 카운터·게시판·rate limit 모두 Upstash. REST API 기반 서버리스 Redis는 일반 Redis와 패턴이 다름.
lib/votes.ts— Redis 클라이언트 초기화lib/board.ts— ZADD · ZRANGE · INCR · EXPIREapp/api/votes/route.ts— GET / POST 핸들러
- Sorted Set (ZADD/ZRANGE) 왜 게시판에 썼나?
- INCR + EXPIRE 조합으로 rate limit 어떻게 동작?
- REST 기반 Redis가 일반 Redis와 다른 점?
- 왜 Vercel KV가 무료 50MB에서 게시판 1만 글까지 OK?
인증·보안
왜 필요? 관리자 토글의 쿠키 기반 인증, rate limiting, 비번 해시·timing safe 비교, XSS 방지(escape) 등 보안 기초.
lib/auth.ts— isAdmin() · cookieAttrs() · checkPassword()app/api/admin/login/route.ts— 비번 검증 + 쿠키 설정 + rate limitcomponents/PostContent.tsx— 본문 HTML escape (XSS 방지)lib/board.ts— checkRateLimit()
- 쿠키의 `httpOnly` `secure` `sameSite` 각각 무엇을 막나?
- 왜 ADMIN_PASSWORD와 ADMIN_TOKEN을 분리? (단순화 가능?)
- Rate limit을 IP 기반으로 하는 것의 한계?
- XSS 방지를 위해 사용자 입력을 어떻게 처리?
SEO
왜 필요? 노잼선거가 5일 만에 구글·네이버 검색 1위 한 핵심. 메타데이터·sitemap·OG 이미지·robots.txt·canonical URL.
app/layout.tsx— 글로벌 metadata (title template, OG, twitter)app/recap/page.tsx— 페이지별 metadata + canonicalapp/opengraph-image.tsx— 동적 OG 이미지app/sitemap.ts— 동적 sitemap.xml 생성
- `alternates.canonical` 왜 중요?
- OG 이미지 1200×630 권장 이유?
- Sitemap.xml과 robots.txt가 SEO에 미치는 영향?
- Vercel의 OG 이미지가 캐시되는 메커니즘?
데이터 처리·정렬·필터링
왜 필요? 6,714명 후보 데이터를 메모리에서 정렬·집계. JavaScript Array methods 숙련도가 통계 페이지 전체 결정.
lib/overallStats.ts— 재산·전과·나이 Top N 함수들lib/districtRank.ts— 구·시·군별 집계 + 랭킹lib/uncontested.ts— 단독후보 추출 (Map + Set)lib/searchCandidates.ts— 한글 자음(초성) 매칭 검색
- `map` / `filter` / `reduce` 각각 언제?
- 한글 초성 추출 어떻게? (`0xAC00`, `588`의 의미)
- Sort 함수의 비교 함수 작성 시 주의점?
- Map과 Object 중 언제 무엇을 쓰나?
UX·UI 패턴
왜 필요? URL hash·query 라우팅, 카드 자동 스크롤, sticky 탭, 다크모드 셀렉트 가독성 등. 작지만 사용자 경험 결정하는 디테일.
components/RaceTabs.tsx— ?cand= 쿼리 자동 스크롤lib/candidateHref.ts— buildCandidateHref 공통 헬퍼components/Marquee.tsx— 무한 스크롤 마키components/RegionCard.tsx— 모달 패턴 (모바일 대응)
- URL `#hash` 와 `?query` 의 차이? 언제 어떤 거?
- Sticky positioning의 `top-0`이 동작 안 할 때 원인은?
- 다크모드에서 native `<select>` 옵션이 흰 배경 나오는 이유? 어떻게 해결?
- Hydration 직전 깜빡임을 어떻게 줄이나?
면접 예상 질문 25개
면접관 입장에서 이 프로젝트로 물어볼 만한 질문들. 답변을 미리 적어두고 시뮬레이션해보세요. ⭐는 자주 받는 핵심 질문.
- ⭐ 노잼선거를 한 문장으로 설명해주세요. (엘리베이터 피치 — "1분만 보면 알 수 있게 만든 시민 선거 사이트")
- ⭐ 왜 이 프로젝트를 시작했어요? (개인 동기 + 시민 가치 둘 다)
- 혼자 만든 거예요? 팀이 몇 명?
- ⭐ 5일 만에 만든 거 맞나요? 어떻게 가능했어요? (AI 활용 솔직히 인정 + 의사결정은 본인)
- ⭐ 왜 Next.js 14 App Router를 선택했나요? Pages Router랑 차이는?
- 왜 Vercel? AWS·GCP 같은 다른 옵션은 고려 안 했나요?
- ⭐ 왜 6,714명 데이터를 정적 JSON으로 했나요? PostgreSQL·MySQL 안 쓰고?
- Upstash Redis와 일반 Redis 차이는? 왜 서버리스 KV?
- Tailwind CSS 선택 이유? styled-components·CSS Modules 비교했어요?
- ⭐ Server Component와 Client Component를 어떤 기준으로 나눴어요?
- 전체 데이터 흐름을 설명해주세요. 사용자 클릭부터 응답까지.
- ⭐ 관리자 인증 시스템 설계를 설명해주세요. (쿠키 + 환경변수 + rate limit)
- 마스킹 시스템의 트레이드오프는? 왜 클라이언트 마스킹이 아니라 서버 마스킹?
- Rate limiting 어떻게 구현했나요? Upstash 사용한 이유?
- ⭐ 트래픽 폭증 시 어떻게 대응했나요? (Vercel Edge + ISR + 모니터링)
- ⭐ 어뷰징 사건이 있었다고 하던데 어떻게 발견·대응했나요? 막을 수 있었나요?
- D-day 카운트다운이 안 바뀌는 버그가 있었다고요? 원인은? 어떻게 해결?
- ⭐ 선관위 데이터가 사라진 후 개인정보 보호 어떻게 처리했나요?
- SSR Hydration mismatch 경험 있나요? 어떻게 디버깅?
- ⭐ 어떻게 트래픽을 모았나요? 광고 없이 3,705명?
- 사용자 피드백을 어떻게 수집·반영했나요?
- SEO를 어떻게 신경 썼나요? 5일 만에 검색 1위가 가능?
- ⭐ AI(Claude Code 등)를 어디까지 활용했나요? 코드를 본인이 짠 게 아니라면 본인 프로젝트라고 할 수 있나요? (가장 첨예한 질문. 솔직 + 의사결정 강조)
- 이 프로젝트의 기술 부채는? 다시 만든다면 무엇을 바꿀 건가요?
- 스케일링 한계는 어디? 10만 명 동시 접속이면?
- STAR 구조: Situation → Task → Action → Result. 사건들 질문(D-day 버그·어뷰징 등)은 이 구조가 잘 맞음.
- 트레이드오프 명시: "왜 X 선택?" → "Y와 비교했을 때 Z 이유로 X. 다만 W는 단점."
- 숫자 인용: "트래픽 3,705명", "5일", "6,714명", "₩2,300/월" — 구체 숫자가 신뢰감.
- AI 활용 솔직히: "프론트 코드는 Claude Code 도움을 많이 받았지만, 아키텍처·데이터 모델·보안 결정·운영은 제가 직접 했습니다." 이게 정답.
- 모르는 거 인정: 모르면 "그 부분은 학습이 더 필요합니다, 다만 이렇게 추정합니다..." 솔직히. 면접관이 더 좋아함.
체크 질문 40개에 본인이 답할 수 있다면 — 노잼선거를 "본인 프로젝트"로 당당히 말할 수 있는 수준이에요. 모든 코드를 본인이 짠 게 아니라도, 모든 결정과 구조를 본인이 설명할 수 있다는 게 중요해요.
공부하다가 막히면 노잼선거 코드 직접 열어서 보거나, 해당 파일을 ChatGPT/Claude에 붙여넣고 "이거 왜 이렇게 짰어?" 물어보세요. AI 시대의 학습 방식.
공부하기 전에 회고나 구성도 다시 보고 싶으면