본문으로 건너뛰기
면접 학습
복습세션 →

과제 기반 — 기차예약

토픽 9 · Phase 2

6시간 트레이드오프·회고

질문 5개
  1. Q9-1

    6시간 안에 무엇을 우선했고 무엇을 일부러 미뤘나요? README to-do에 적힌 두 항목("필수 데이터 없을 시 redirect 가드", "테스트 코드")의 미구현 근거가 궁금합니다.

    핵심 포인트

    • 우선한 것: **골격(편도/왕복 분기, 다시 선택 영속화, 인원·날짜 검증)**과 타입·컴포넌트 분해.
    • 미룬 것: redirect 가드, 테스트 코드, isHttpError 활용, mutation onError 토스트.
    • 우선 결정의 잣대: "합리적 가설"을 세워 골격이 흔들리지 않게 만든다(토스 가이드).
    • 미룸의 형태: 회피가 아니라 README to-do로 명시 — 토스 가이드 "사용자 경험 개선 지점은 README 등에 문서화"와 직접 정렬.
    모범 답안먼저 답해보고 펼치기

    6시간이라는 제약이 평가의 일부라고 받아들였어요. 그래서 시간 분배 기준을 의식적으로 정하고 일했습니다.

    우선순위 결정 기준은 토스 PDF의 "스스로 합리적인 가설을 세우고 계속 진행"이었어요. "이 과제에서 평가의 핵심이 무엇일까"를 세 가지로 가설을 세웠습니다 — (1) 시작점에 비워둔 인라인 코드를 어떻게 분해하느냐, (2) 라이브러리 선택과 그 의사결정 트레이스, (3) PDF에 명시된 미세 룰("기차표 제외 입력값 유지", "0인 인원 표시 제외" 등)을 빠뜨리지 않느냐. 이 셋을 우선순위 상위에 두고 시간을 썼습니다.

    그래서 우선한 것들 — 컴포넌트 분해(SearchPage 인라인 185줄 → 43줄), 상태관리 의사결정(jotai + react-query 분리, atom 파일 두 개로 분리), 편도/왕복 분기(한 라우트 + 모드 전환, discriminated union), 검증 정책(BottomSheet 단·atom 단·derived atom 단의 책임 분리), 타입스크립트 도메인 모델링(types/ 5개 entity 분리). 골격이 흔들리지 않게 만드는 데 시간을 집중했어요.

    미룬 것 — 새로고침 시 필수 데이터 없을 시 SearchPage redirect 가드, 테스트 코드, isHttpError + mutation onError 토스트. 이 셋은 골격이 안정된 뒤 위에 얹는 layer라고 봤어요. 골격을 어설프게 만든 채로 위 layer를 쌓으면 골격이 바뀔 때 위 layer를 다시 짜야 하니까요.

    미룬 것을 README to-do로 명시한 게 의식적인 결정이었습니다. 토스 PDF의 "사용자 경험 개선 지점은 README 등에 문서화" 가이드라인과 정확히 정렬되는 부분이에요. 회피가 아니라 우선순위 결정의 결과로 미룬 것이고, 인터뷰에서도 그렇게 답할 준비가 됐습니다.

    이 답변, 어땠나요?

    꼬리 질문
    • Q9-1-a. 시간이 1~2시간 더 있었다면 무엇부터 했을까요? → 첫 번째 우선순위는 redirect 가드예요. CompletePage에서 이미 비슷한 패턴(useEffect에서 reservation null이면 navigate)을 쓰고 있어서, 이걸 라우트 가드 컴포넌트로 일반화하는 데 30분~1시간이면 충분합니다. 두 번째는 mutation onError + isHttpError 분기. 사용자에게 에러를 알려주는 토스트가 들어가면 신뢰감이 크게 올라가요. 세 번째는 테스트 코드 — 검증 정책(isSearchValidAtom의 7분기, setDepartureDateAtom의 cross-atom side effect)부터 단위 테스트로 잡으면 atom 모델링의 회귀를 막을 수 있습니다.

    • Q9-1-b. 테스트 코드가 빠진 게 가장 큰 약점 같은데, 이 자리에서 어떻게 보완하겠다고 답하실 건가요? → 솔직히 인정합니다. 6시간 안에 검증 정책·atom side effect·discriminated union 분기 같은 핵심 로직의 테스트를 짜지 못한 건 약점이에요. 다만 어디에 어떤 테스트를 넣을지는 분명한 방향이 있어요 — (1) isSearchValidAtom 7분기는 jotai의 store API로 테스트 가능, (2) setDepartureDateAtom의 cross-atom side effect도 같은 방식, (3) formatPassengerCount/formatDate/formatDuration 같은 utils 순수 함수는 단위 테스트가 가장 가벼움, (4) UI 테스트는 react-testing-library로 SearchPage의 입력 → 검색 버튼 활성화 흐름 정도. 우선순위는 atom → utils → UI 순으로 갈 거예요.

    • Q9-1-c. "익숙한 방법"이라는 PDF 가이드라인이 어떻게 의사결정에 영향을 줬나요? → 이걸 잣대로 자주 썼어요. 예를 들어 — query key factory 패턴 도입을 검토할 때, 4개 API에는 over-engineering이라 단순 상수 객체로 갔고; 변환 layer(DTO ↔ Domain) 도입도 4개 API 규모에서는 디버깅 비용만 키울 거라 안 뒀고; EmptyResult 외 공용 컴포넌트 추출도 미래 재사용 가설에 의존하지 않고 두 곳 이상 등장할 때만 미뤘어요. "고급 패턴은 정말 필요할 때 쓰는 도구지 미리 까는 인프라가 아니다"라는 입장을 토스 가이드라인이 정당화해줬다고 생각해요.

    CS · 이론
    • Time-boxed Development & 우선순위 결정: 6시간 같은 명시적 제약 안에서는 "무엇을 안 하느냐"의 결정이 "무엇을 하느냐"만큼 중요. 미루는 결정도 명시적으로 보여줘야 회피가 아닌 의식적 결정으로 인식됨.
    • YAGNI (You Aren't Gonna Need It): extreme programming의 원칙. 미래에 필요할 수도 있는 것을 미리 만들지 말라. 6시간 제약에서 특히 강하게 적용.
    • Documentation-Driven Reasoning: 못 한 일을 README에 명시함으로써 (1) 다음 작업자에게 컨텍스트 전달, (2) 의사결정 근거 보존, (3) 회피와 의도적 미룸의 구분. 토스 가이드라인이 이 원칙을 강조.
  2. Q9-2

    6시간 동안 작업하면서 가장 어려웠던 의사결정은 뭐였나요? 그리고 그걸 어떻게 풀었나요?

    핵심 포인트

    • 가장 어려웠던 결정: 상태관리 라이브러리 선택package.json이 비어 있는 상태에서 어떤 도구로 갈지.
    • 후보: jotai / zustand / Context API / Redux Toolkit (현실적으로 jotai vs zustand의 양자택일).
    • 결정 근거: (1) 검색 조건 6개 atom의 입자도, (2) cross-atom side effect 표현(setDepartureDateAtom), (3) derived atom의 자동 의존 추적(isSearchValidAtom).
    • 보조 어려움: 편도/왕복 분기를 한 라우트로 갈지 두 라우트로 갈지.
    모범 답안먼저 답해보고 펼치기

    가장 시간을 많이 들인 결정이 두 가지 있었어요.

    첫 번째는 상태관리 라이브러리 선택. package.json에 jotai도 react-query도 zustand도 없어서, 빈 캔버스에서 시작해야 했습니다. 직관은 "atom-based가 이 도메인에 맞을 것 같다"였는데 — 검증할 근거를 세 가지로 정리했어요.

    (1) 입자도(granularity): 검색 조건이 6개 필드(출발역, 도착역, 여행 방식, 가는날, 오는날, 인원수)로 나뉘는데, zustand로 한 store에 다 모으면 한 필드가 바뀔 때 모든 구독자가 영향을 받아요. zustand의 selector로 회피 가능하지만 selector boilerplate가 매 필드마다 들어가게 됩니다. jotai는 atom 단위로 자연스럽게 입자도가 결정돼요.

    (2) cross-atom side effect: 가는날이 바뀌면 오는날을 검사·보정하고 선택 티켓을 reset하는 로직 — 이건 특정 atom의 write 시점에 다른 atom들을 건드리는 패턴인데, jotai의 write-only atom(setDepartureDateAtom)으로 자연스럽게 표현됩니다. zustand에서는 store 액션 안에서 다른 슬라이스를 건드리는 코드가 되는데, 슬라이스가 분리돼 있다면 더 복잡해져요.

    (3) derived atom의 자동 의존 추적: isSearchValidAtom이 6개 atom에 의존하는데, jotai는 get(atomA) 호출만으로 의존성이 자동 등록됩니다. zustand에서 같은 일을 하려면 selector를 직접 짜야 하고, 의존성을 빠뜨리면 stale value 위험이 있어요.

    이 세 가지 근거로 jotai로 결정. zustand가 더 단순한 모델이라 작은 프로젝트에 매력적이긴 한데, 이번 과제 도메인에는 jotai의 atom 모델이 더 정확히 맞물린다고 봤어요.

    두 번째 어려움은 편도/왕복 분기를 한 라우트로 갈지 두 라우트로 갈지였어요. 두 라우트(/tickets/outbound, /tickets/inbound)로 가면 모든 분기가 URL로 명시되어 추적이 쉽지만 — 컴포넌트 트리·atom 접근·navigate 흐름이 다 두 개씩 깔리게 됩니다. 한 라우트(/tickets) + isReturnPhase 모드 전환으로 가면 트리·atom·navigate가 단순해지지만 시각적으로 같은 화면이 두 번 등장하는 모호함이 있어요. 결국 단순함을 우선해서 한 라우트로 갔고, 시각적 모호함은 README to-do(transition·인디케이터)로 미뤘습니다.

    이 답변, 어땠나요?

    꼬리 질문
    • Q9-2-a. 만약 zustand로 갔다면 어디가 가장 다르게 풀렸을 것 같나요?isSearchValidAtom 자리가 가장 다르게 풀렸을 거예요. zustand에서는 useSearchStore(state => isSearchValid(state)) 같은 selector 함수를 직접 짜야 하고, 7개 분기 검증 로직을 selector 안에 넣어야 합니다. 의존성 추적이 자동이 아니라 selector 안에서 어떤 state를 읽는지가 곧 의존성이라, 분기를 추가할 때 빠뜨리지 않도록 신경 써야 해요. jotai의 derived atom이 같은 일을 더 가볍게 표현해서 그쪽으로 갔습니다.

    • Q9-2-b. 의사결정 과정에서 직관과 분석이 충돌한 적이 있었나요? → 직관은 처음부터 jotai였는데, 이걸 그대로 따르지 않고 위 세 가지 근거를 명시적으로 정리한 게 의식적인 단계였어요. 인터뷰에서 "왜 jotai인가"를 물었을 때 "익숙해서"가 아니라 "도메인 특성 때문에"라고 답할 수 있도록요. 토스 가이드의 "스스로 합리적 가설을 세우고 진행" 원칙이 직관을 검증 가능한 가설로 바꾸는 단계를 강조하는 것 같아서, 그걸 따라간 셈입니다.

    • Q9-2-c. 6시간 안에서 의사결정에 너무 많은 시간을 쓰면 구현이 못 따라가지 않나요? → 그 위험을 의식했어요. 실제로 위 두 결정에 약 30~40분 정도 썼는데, 이 시간이 후속 6시간 동안 회수됐다고 봅니다. atom 모델이 도메인과 잘 맞물리니까 cross-atom side effect나 reset 패턴이 자연스럽게 풀렸고, 한 라우트 모델은 컴포넌트 분해를 단순하게 만들어서 ConfirmPage·CompletePage 작업 시간을 줄여줬어요. 의사결정에 30분을 쓰는 게 잘못된 길로 1시간 가는 것보다 나은 투자라는 판단이었습니다.

    CS · 이론
    • Hypothesis-Driven Development: 직관을 명시적 가설로 바꾸고 검증 기준을 세우는 의사결정 방식. 시간 제약이 있을 때 잘못된 길로 가는 위험을 줄여줍니다.
    • Library Selection의 다축 평가: 한 축(예: 인기도·번들 크기)으로만 보지 않고 도메인 적합성·팀 익숙함·생태계 지원 등 여러 축으로 평가. 이번 과제에서는 도메인 특성이 가장 큰 축이었음.
    • Sunk Cost Avoidance: 한 번 결정한 라이브러리·아키텍처에 시간을 많이 썼다고 그걸 고집하지 말라. 6시간 안에서 잘못됐다고 판단되면 빠르게 되돌아오는 게 낫습니다.
  3. Q9-3

    토스 PDF 가이드라인 중 답변에 가장 자주 인용한 게 "화려한 방법보다 평소에 익숙한 방법"이었는데요, 이걸 어떻게 해석했고 왜 자주 인용했나요?

    핵심 포인트

    • 직관적 의미: "fancy한 추상화·라이브러리·패턴을 쓰지 말고, 익숙하고 검증된 도구로 풀어라."
    • 더 깊은 의미: "본질적이지 않은 복잡성을 만들지 마라." — Brooks의 essential vs accidental complexity 구분.
    • 이 가이드라인이 의사결정에 끼친 영향: (1) query key factory 미도입, (2) DTO ↔ Domain 변환 layer 미도입, (3) 공용 컴포넌트 추출 보수적, (4) useTempState 같은 hook 추상화 미적용 (Rule of Three).
    • 토스 환경에서 일하는 방식의 시그널 — "익숙한" 도구가 곧 "팀이 협업 가능한" 도구.
    모범 답안먼저 답해보고 펼치기

    이 가이드라인을 처음 봤을 때는 "고급 기법을 쓰지 말라"는 단순한 의미로 읽었어요. 그런데 작업하면서 더 깊은 의미를 발견했습니다.

    표면적 의미는 — fancy한 추상화·라이브러리·패턴을 쓰지 말라는 것. 예를 들어 query key factory 패턴은 react-query 커뮤니티에서 권장되는 멋진 패턴이긴 한데, 4개 API에 도입하면 코드가 무거워져요. 변환 layer(DTO ↔ Domain)도 마찬가지고, 공용 컴포넌트 무리한 추출도 그렇습니다. 이런 패턴들의 가치는 큰 코드베이스에서 발휘되는 거지, 6시간짜리 과제에서는 비용만 큽니다.

    더 깊이 들어가면 — Fred Brooks의 "No Silver Bullet" 에세이의 essential vs accidental complexity 구분과 닿아 있다고 봤어요. 도메인의 본질적 복잡성(편도/왕복 분기, 인원 검증, 다시 선택 영속화)은 피할 수 없지만, 추상화·도구 선택에서 만들어지는 우발적 복잡성은 피할 수 있어요. 이 가이드라인은 "essential complexity는 정확히 다루되, accidental complexity는 만들지 말라"는 의미로 읽힙니다.

    또 하나 — 토스 환경에서 일하는 방식의 시그널이라고 봤어요. "익숙한" 도구가 곧 "팀이 협업 가능한" 도구라는 뜻이거든요. 내가 멋지게 추상화한 hook을 새로 짠 사람이 이해하지 못하면, 그 hook은 팀 전체의 자산이 아니라 내 부채입니다. "익숙한 방법"은 팀의 mental model을 존중하는 자세에 대한 메타시그널이라고 해석했어요.

    이 해석으로 의사결정 잣대를 세우니까 작업이 훨씬 빨라졌어요. 매 결정마다 "이게 본질적 복잡성을 다루는가, 우발적 복잡성을 만드는가"를 묻고, 후자면 단호하게 미루거나 안 했습니다. 토스 가이드라인이 단순히 "쓸데없이 복잡하게 짜지 마"가 아니라 그 잣대 자체였어요.

    이 답변, 어땠나요?

    꼬리 질문
    • Q9-3-a. "익숙한 방법"이 너무 보수적이라 진짜 좋은 패턴까지 못 쓰게 만드는 건 아닐까요? → 그 위험은 있어요. 그래서 잣대를 "익숙하지 않은 패턴인가"가 아니라 "이 패턴이 essential complexity를 다루는가"로 해석했습니다. discriminated union이나 derived atom은 일부에게는 익숙하지 않을 수 있지만, 도메인 복잡성(편도/왕복 분기, 6필드 검증)을 정확히 표현하는 도구라 정당화돼요. 익숙함의 잣대가 도그마가 되면 안 되고, 도메인 적합성이 더 우선이라고 봤습니다.

    • Q9-3-b. 그럼 어떤 패턴이 "fancy한 추상화"의 사례인지 한두 개만 들면요? → query key factory가 가장 명확한 사례예요. 객체 함수 트리로 key를 관리하는 패턴인데 — "객체 함수 트리"라는 표현 자체가 "이게 왜 필요한가?"를 물어야 하는 단계로 한 단계 들어가게 만듭니다. 4개 API에 적용하면 코드 줄 수가 두 배가 되고 인지 부담도 두 배가 돼요. 또 하나는 — overlay-kit을 쓰지 않고 manual portal + transition + focus management까지 직접 짜는 길도 있는데, 이건 라이브러리가 깔아주는 걸 직접 깔아주는 매우 fancy한 추상화입니다.

    • Q9-3-c. 토스의 "익숙한"의 기준이 회사마다 다를 텐데, 이번 과제에서 가정한 "익숙함"은 뭐였나요? → 모던 React 생태계의 표준 — Hooks·React Query·Jotai 같은 것 — 까지를 익숙함의 범위로 가정했어요. 그 너머의 메타-패턴(query key factory, anti-corruption layer, render props 같은 디자인 패턴)은 fancy로 분류했습니다. 이 분류가 정답은 아닐 수 있는데, 토스 환경의 "표준"을 정확히 모르니까 보수적으로 잡은 거예요. 만약 토스 팀에 합류한다면 그 팀의 컨벤션을 빠르게 파악하는 게 우선이고, 거기에 맞춰 잣대가 다시 조정될 거라고 봅니다.

    CS · 이론
    • Essential vs Accidental Complexity (Fred Brooks): 도메인이 본질적으로 갖는 복잡성과, 도구·언어·추상화 선택으로 우리가 만들어내는 복잡성의 구분. 좋은 엔지니어링은 후자를 최소화.
    • Boring Technology Manifesto (Dan McKinley): "지루한 기술을 골라라." 새 기술의 이득보다 익숙한 기술의 안정성·예측 가능성이 큰 경우가 많음.
    • Cognitive Load (John Sweller): 코드를 읽고 이해하는 데 드는 인지 부담. 팀이 큰 코드베이스를 다룰 수 있게 하려면 cognitive load를 의식적으로 관리해야 함.
  4. Q9-4

    시간이 더 있다면 가장 먼저 무엇을 손볼 건가요? 우선순위 1~3위와 그 근거를 들려주세요.

    핵심 포인트

    • 1위: 라우트 가드<RequireSearchCondition>, <RequireSelectedTicket> wrapper로 새로고침 시 redirect. CompletePage 패턴을 일반화하면 30분~1시간.
    • 2위: mutation onError + isHttpError 토스트 — 사용자 친화 에러 메시지. PDF 명시 패턴이라 평가 점수 회수.
    • 3위: 핵심 atom·utils 단위 테스트isSearchValidAtom 7분기, setDepartureDateAtom cross-atom side effect, formatPassengerCount/formatDuration/formatTime 순수 함수.
    • 우선순위 잣대: 기능 안정성 → 사용자 신뢰 → 회귀 방지 순.
    모범 답안먼저 답해보고 펼치기

    시간이 1~2시간 더 있다면 손볼 우선순위는 명확해요.

    1위는 라우트 가드. README to-do의 첫 항목인 "각 페이지에서 사용하는 필수 데이터가 없을 경우 search page로 redirect 처리. (URL로 바로 접근 방지)"가 이 자리예요. 새로고침이나 deep link로 ConfirmPage·CompletePage·TicketsPage에 들어왔을 때, 필수 atom이 비어 있으면 SearchPage로 redirect하는 가드가 필요합니다. CompletePage에 이미 useEffect로 같은 패턴을 써놨어서 — 이걸 <RequireSearchCondition>, <RequireSelectedTicket> 같은 wrapper 컴포넌트로 추출하면 30분~1시간이면 끝나요. 가장 가성비 좋은 보강이라고 봤습니다.

    2위는 mutation onError + isHttpError 토스트. PDF에 isHttpError 활용 예시가 명시돼 있어서 평가에서 짚을 가능성이 높아요. useReservationMutation의 onError에서 isHttpError로 분기해 status code 기반 토스트(409: "이미 예약된 좌석", 5xx: "잠시 후 재시도", 그 외: error.message)를 띄우는 흐름. 1시간이면 mutation 코드 + Toast 호출 + 테스트까지 끝납니다. 기능 안정성이라기보다 사용자 신뢰 차원의 보강이에요.

    3위는 핵심 atom·utils 단위 테스트. README to-do 두 번째 항목이에요. 우선순위는 (1) isSearchValidAtom 7분기 — jotai store API로 atom write 후 derived atom 결과를 assert. (2) setDepartureDateAtom cross-atom side effect — 가는날 변경 시 오는날·티켓 reset 동작 확인. (3) formatPassengerCount/formatDuration/formatTime 순수 함수 — 가장 가벼운 단위 테스트라 빠르게 커버. 2~3시간이면 핵심 회귀 방지선이 만들어져요.

    우선순위 잣대는 — 기능 안정성(redirect 가드) → 사용자 신뢰(에러 토스트) → 회귀 방지(테스트) 순입니다. 사용자가 깨진 화면을 보지 않게 하는 게 1순위, 무슨 일이 일어났는지 알게 하는 게 2순위, 미래 변경에서 안전망을 만드는 게 3순위라는 직관이에요.

    이 답변, 어땠나요?

    꼬리 질문
    • Q9-4-a. 라우트 가드 대신 atomWithStorage로 persist를 도입하는 길도 있을 텐데, 왜 가드를 우선하셨어요? → ROI 차이예요. atomWithStorage는 도입 시 검색 조건의 Date 타입 직렬화/역직렬화, schema validation, hydration 타이밍 같은 부수 결정이 다 따라옵니다. 1시간 안에 끝나기 어렵고 buggy해질 위험이 높아요. 가드는 30분~1시간 안에 깨끗하게 풀리고 같은 사용자 경험(새로고침 시 안전한 화면)을 줍니다. 시간이 더 있다면 그 다음 단계로 atomWithStorage를 검토할 수 있어요.

    • Q9-4-b. 테스트가 3순위인 게 좀 의외예요. 보통 테스트가 1순위로 와야 하지 않나요? → 일반적으로는 그런데, 현재 코드에는 사용자에게 보이는 미완성 영역(redirect 가드 부재, 에러 미처리)이 있어서 그것부터 메우는 게 맞다고 봤어요. 테스트는 회귀 방지 도구라, 회귀할 코드가 어느 정도 안정된 후에 의미가 큽니다. 라우트 가드와 에러 처리가 들어간 뒤에 테스트로 굳히는 흐름이 가장 자연스러워요. 1주일 단위 작업이라면 테스트가 1순위에 와도 무방합니다.

    • Q9-4-c. 4순위, 5순위까지 더 들어간다면 무엇이 올까요? → 4순위는 로딩·빈 상태 UI 정리keepPreviousData 시 placeholder 시각적 시그널, isLoading 시 skeleton 도입 같은 polish. 5순위는 검색 디바운스useStationsQuery가 글자 입력마다 fetch가 일어나니까 lodash debounce나 use-debounce hook으로 300ms 정도 묶어서 네트워크 호출을 줄이는 것. 두 가지 다 사용자 perceived performance에 직접 영향을 줍니다.

    CS · 이론
    • Cost-Benefit Analysis 관점의 우선순위: ROI(투자 대비 효과)를 잣대로 작업 우선순위를 정하면 시간 제약 안에서 가장 큰 가치를 회수할 수 있음. 30분 작업으로 큰 사용자 경험 개선이 일어나는 항목이 1순위.
    • Test Pyramid (Mike Cohn): 단위 테스트 → 통합 테스트 → E2E 테스트 순으로 가벼움. 시간 제약에서는 단위 테스트(atom·utils)부터 까는 게 ROI가 가장 좋음.
    • Defense in Depth: 보안·안정성 분야의 원칙. 한 layer가 뚫려도 다른 layer가 막아주는 안전망. redirect 가드 + 에러 토스트 + 테스트 = 세 layer 안전망의 단순화 버전.
  5. Q9-5

    이번 과제를 회고하면, 더 잘할 수 있었던 부분이 어디라고 보세요? 그리고 그게 토스에서 일하는 데 어떤 의미를 갖나요?

    핵심 포인트

    • enum vs as const 일관성 부족PASSENGER_TYPE/TRIP_TYPE/STATION_TYPE은 enum, TICKET_STATUS는 as const로 혼재 — 컨벤션 미정리.
    • isHttpError 미활용 — PDF 명시 패턴인데 react-query에 맡기고 직접 호출 안 함.
    • 테스트 부재 — 검증 정책·atom 모델링이 단위 테스트로 안 굳혀진 채 제출됨.
    • 시각적 모호함: TicketsPage 한 라우트 + 모드 전환의 UI 시그널(애니메이션·인디케이터) 부족.
    • 토스에서 일하는 의미: 자기 의사결정을 트레이스할 수 있고, 미흡한 부분을 정직하게 볼 줄 안다는 자세 — 토스 컬쳐의 핵심 가치.
    모범 답안먼저 답해보고 펼치기

    회고는 솔직하게 정리할게요. 미흡했던 부분이 분명히 있어요.

    enum vs as const 일관성 부족. types/ 안에서 PASSENGER_TYPE, TRIP_TYPE, STATION_TYPE은 enum으로, TICKET_STATUSas const 패턴으로 갔어요. 처음부터 컨벤션을 정하고 시작했어야 했는데, 작업 흐름 따라 그때그때 결정한 결과예요. 일관성이 코드 베이스의 자산인데 제 시작이 그걸 제대로 못 챙긴 부분입니다. 통일한다면 as const 쪽이 zero-runtime cost와 tree shaking 호환성에서 더 모던한 선택이라고 봐요.

    isHttpError 미활용. PDF에 명시된 패턴인데 react-query 자동 에러 관리에 맡기고 직접 호출을 안 했어요. 6시간 안에서 우선순위를 미룬 결정인데 — 결과적으로 사용자가 에러를 만났을 때 친절한 안내가 없는 미완성 상태로 제출됐습니다. README에 to-do로 명시했지만, 인터뷰에서는 회피하지 않고 인정하기로 했어요.

    테스트 부재. 검증 정책(isSearchValidAtom 7분기)이나 atom의 cross-atom side effect(setDepartureDateAtom)는 단위 테스트로 굳혀야 회귀 안전성이 생기는데, 시간 우선순위에서 빼버렸어요. 코드를 짤 때 이미 "이건 테스트 가치가 큰 영역이다"라고 의식했으면서도 못 했다는 게 약점입니다.

    시각적 모호함. TicketsPage 한 라우트 + 모드 전환을 단순함 우선으로 결정했는데, 사용자에게 "지금 가는편을 보고 있는지 오는편을 보고 있는지" 시각적 시그널(transition·인디케이터)이 부족합니다. 단순함의 trade-off를 UX 보강으로 메우는 결정이 또 README to-do로 미뤄졌어요.

    이걸 토스에서 일하는 맥락으로 보면 — 자기 의사결정을 트레이스할 수 있고, 미흡한 부분을 정직하게 볼 줄 아는 자세가 중요하다고 봐요. 토스가 강조하는 가치 중 하나가 "정직"인 걸로 알고 있고, 이번 과제도 6시간 안에 모든 걸 완벽히 만든 척하기보다는 "이건 했고 이건 못 했다, 그 근거는 이거였다"를 명시하는 게 더 신뢰감을 주는 답이라고 생각합니다. 인터뷰에서도 같은 자세로 답할 준비가 됐어요.

    이 답변, 어땠나요?

    꼬리 질문
    • Q9-5-a. 약점을 솔직히 말하는 건 좋지만, 면접에서 너무 많이 인정하면 약하게 보이지 않을까요? → 그 선이 있어서 의식적으로 정리했어요. 약점을 인정하되, (1) 왜 그렇게 됐는지의 트레이드오프 근거, (2) 어떻게 보완할지의 구체적 계획을 함께 답하기로요. "테스트 못 했어요"가 아니라 "테스트는 우선순위에서 뺐고, 시간이 더 있다면 atom store API로 isSearchValidAtom의 7분기부터 단위 테스트로 잡을 거예요"가 답이에요. 인정 + 근거 + 계획 세 단계가 갖춰지면 신뢰감을 잃지 않습니다.

    • Q9-5-b. 가장 잘했다고 생각하는 부분은요?atom 파일을 두 개로 분리한 결정이라고 봐요. 이게 단순한 폴더 분리가 아니라 — 토스 PDF 명시 요구사항("기차표 제외 입력값 유지")을 도메인 라이프사이클 관점으로 풀어낸 의사결정이거든요. 이 분리 덕에 (1) "다시 선택" CTA가 두 줄로 끝났고, (2) resetSelectedTickets vs resetAll 두 reset 함수가 사용자 mental model 차이를 코드에 그대로 표현했고, (3) 미래 정책 변경(예약 후 검색 조건 유지 여부)이 한 줄 변경으로 끝납니다. atom의 단순한 도구를 도메인 의도와 정확히 맞물리게 만든 게 가장 만족스러운 부분이에요.

    • Q9-5-c. 토스에 합류한다면, 이번 과제에서 배운 걸 어떻게 적용하고 싶나요? → 두 가지를 가져갈 거예요. 첫째, 시작점에 비워둔 자리의 의도를 읽는 습관. 토스 코드베이스도 분명히 "이 자리는 나중에 채워질 자리"가 있을 텐데, 그 의도를 읽어내는 게 합류 후 빠르게 기여할 수 있는 길이라고 봅니다. 둘째, trade-off를 명시적으로 문서화하는 습관. 이번 과제에서 README to-do를 명시한 결정이 회피가 아닌 결정으로 받아들여지길 바랐듯, 팀에서도 "왜 이렇게 결정했는지"를 코드 리뷰·PR 설명·문서로 남기는 자세가 협업에서 큰 가치를 만든다고 생각해요.

    CS · 이론
    • Retrospective의 가치: agile에서 retrospective는 기능 완성만큼 중요한 자산. 팀이 같은 실수를 반복하지 않게 만드는 학습 메커니즘. 개인 회고도 같은 가치.
    • Honest Self-Assessment: 자기 약점을 정확히 보는 능력 = 성장의 토대. 약점을 숨기면 같은 자리에 머무르고, 인정하면 다음 단계로 갈 수 있음.
    • Trade-off Documentation: ADR(Architecture Decision Record) 같은 형식으로 의사결정의 근거·대안·trade-off를 문서화하는 패턴. 시간이 지나도 "왜 이렇게 됐지?"가 추적 가능하게 만듦.