Q7-1
`types/` 폴더에 station, ticket, reservation, trip, passenger 5개 파일을 두셨는데, 분리 기준은 뭐였나요?
핵심 포인트
- 하나의 도메인 entity = 하나의 파일 — 데이터 모양과 그에 결합된 enum/유틸이 같은 개념을 표현하면 한 파일로 묶음.
- 5개 파일이 각각 표현하는 entity: 역(
station), 기차표(ticket), 예약(reservation), 여행 방식(trip), 인원수(passenger). reservation은Pick<Ticket>,Station,PassengerCount,TRIP_TYPE을 합성 — 도메인 의존성을 import로 표현.- 화면이 아닌 도메인 entity 단위로 나눔 — 화면이 늘어나도 type 파일은 그대로.
모범 답안먼저 답해보고 펼치기
타입을 어디에 두느냐는 코드의 첫인상을 결정한다고 봅니다. 그래서 types/ 분리 기준을 의식적으로 정했어요.
기준은 하나의 도메인 entity를 한 파일에 모은다입니다. 예를 들어 station.ts에는 Station 인터페이스와 STATION_TYPE enum이 같이 들어가요. 둘 다 "역"이라는 entity의 일부니까요. ticket.ts에는 Ticket 인터페이스와 TICKET_STATUS const + TicketStatus 타입을 묶습니다. passenger.ts에는 PASSENGER_TYPE enum, PassengerCount 인터페이스, INITIAL_PASSENGER_COUNT 상수, MAX_PASSENGER_TOTAL 상수, totalPassengers/formatPassengerCount 함수까지 함께 담겨요. 인원이라는 도메인을 다루는 데 필요한 모든 단위가 한 파일에 있습니다.
이렇게 도메인 단위로 나누면 import 그래프가 의미 있게 그려져요. reservation.ts를 보면 import { PassengerCount } from './passenger', import { Station } from './station', import { Ticket } from './ticket', import { TRIP_TYPE } from './trip' — 예약이라는 entity가 어떤 entity들의 합성으로 만들어지는지가 import만 봐도 보입니다. 이게 도메인 모델 다이어그램 역할을 하는 셈이에요.
화면이 아니라 도메인 단위로 나눈 게 중요한 결정이었습니다. 만약 화면 단위로 — 예를 들어 searchTypes.ts, confirmTypes.ts — 나눴다면 같은 Station을 SearchPage용, ConfirmPage용으로 두 번 정의하거나 cross-import가 어지러워졌을 거예요. 도메인 단위는 화면이 추가/제거돼도 변하지 않는 안정적 축이라 나누기에 딱 맞았습니다.
이 답변, 어땠나요?
꼬리 질문
-
Q7-1-a. utils와 types를 한 파일에 둔 게 좀 어색한데요?
passenger.ts에totalPassengers함수가 들어가 있어요. → 의식적으로 그 결정을 했습니다.totalPassengers나formatPassengerCount는 PassengerCount 데이터 위에서만 의미 있는 함수예요. 도메인 entity와 함께 두면 entity가 자신을 다루는 함수까지 캡슐화하는 모양이 되어, 사용처에서import { PassengerCount, totalPassengers, formatPassengerCount } from 'types/passenger'한 줄로 다 가져올 수 있습니다. utils로 빼면 같은 도메인이 두 폴더로 흩어지는 비용이 더 큽니다. 다만date.ts처럼 특정 도메인에 묶이지 않는 일반 유틸은 utils에 두는 식으로 구분했어요. -
Q7-1-b.
Station은 그냥{ id, name }두 필드뿐인데 굳이 따로 인터페이스로 둘 필요가 있나요? → 두 필드뿐이라도 따로 두는 게 맞다고 봤습니다. 첫째, 도메인 entity의 이름이 코드 곳곳에 등장하면 의도가 분명해져요 —Station[]vsArray<{ id: number; name: string }>. 둘째, 미래에Station이 더 많은 필드를 가지게 됐을 때 — 영문명, 코드, 좌표 등 — 한 군데만 고치면 모든 사용처가 자동 반영됩니다. Naming things가 cache invalidation만큼 어렵다는 격언이 있을 정도로 이름의 가치가 큰데, 도메인 entity는 그 이름의 가장 단단한 닻이에요. -
Q7-1-c. enum을 쓴 곳(
PASSENGER_TYPE,TRIP_TYPE,STATION_TYPE)과as const를 쓴 곳(TICKET_STATUS)이 섞여 있는데 이유가 있나요? → 처음 작성 시 일관성 측면에서는 한쪽으로 통일하는 게 더 좋았을 거 같아요.as const+(typeof X)[keyof typeof X]패턴은 tree shaking과 컴파일 결과(런타임 객체가 따로 안 만들어짐) 측면에서 enum보다 가볍습니다. enum은 isolated modules·tree shaking에 약점이 있어서 최근 TypeScript 컨벤션은as const쪽을 권장하는 흐름이에요. 이번 과제에서는 enum과 as const가 혼재한 건 사실인데, 둘 다 만들 때의 결정이 일관되지 못했던 부분이라 인터뷰에서 솔직하게 인정하고 "통일한다면 as const 쪽으로" 답변할 생각입니다.
CS · 이론
- Domain-Driven Design의 entity 개념: 도메인 모델링에서 entity는 "ID로 식별 가능한 비즈니스 개념" — 역, 기차표, 예약 모두 ID를 갖는 entity입니다. 이걸 한 파일에 모으는 게 도메인 단위 모듈화의 자연스러운 출발점.
- TypeScript enum vs
as const: enum은 런타임 객체를 만들어 tree shaking에 약점이 있고, isolated modules에 호환 이슈.as const는 zero-runtime cost — 최근 권장 컨벤션입니다. - Module의 cohesion: 한 파일에 들어간 코드가 같은 변경 이유를 공유해야 함. PassengerCount 모양이 바뀌면
INITIAL_PASSENGER_COUNT,totalPassengers,formatPassengerCount가 같이 바뀌니까 한 파일이 정답.