8주차 과제 체크포인트
기본 과제
필수
- 반복 유형 선택
- 일정 생성 또는 수정 시 반복 유형을 선택할 수 있다.
- 반복 유형은 다음과 같다: 매일, 매주, 매월, 매년
- 31일에 매월을 선택한다면 -> 매월 마지막이 아닌, 31일에만 생성하세요.
- 윤년 29일에 매년을 선택한다면 -> 29일에만 생성하세요!
- 반복 일정 표시
- 캘린더 뷰에서 반복 일정을 시각적으로 구분하여 표시한다.
- 아이콘을 넣든 태그를 넣든 자유롭게 해보세요!
- 캘린더 뷰에서 반복 일정을 시각적으로 구분하여 표시한다.
- 반복 종료
- 반복 종료 조건을 지정할 수 있다.
- 옵션: 특정 날짜까지, 특정 횟수만큼, 또는 종료 없음 (예제 특성상, 2025-06-30까지)
- 반복 일정 단일 수정
- 반복일정을 수정하면 단일 일정으로 변경됩니다.
- 반복일정 아이콘도 사라집니다.
- 반복 일정 단일 삭제
- 반복일정을 삭제하면 해당 일정만 삭제합니다.
선택
- 반복 간격 설정
- 각 반복 유형에 대해 간격을 설정할 수 있다.
- 예: 2일마다, 3주마다, 2개월마다 등
- 예외 날짜 처리:
- 반복 일정 중 특정 날짜를 제외할 수 있다.
- 반복 일정 중 특정 날짜의 일정을 수정할 수 있다.
- 요일 지정 (주간 반복의 경우):
- 주간 반복 시 특정 요일을 선택할 수 있다.
- 월간 반복 옵션:
- 매월 특정 날짜에 반복되도록 설정할 수 있다.
- 매월 특정 순서의 요일에 반복되도록 설정할 수 있다.
- 반복 일정 전체 수정 및 삭제
- 반복 일정의 모든 일정을 수정할 수 있다.
- 반복 일정의 모든 일정을 삭제할 수 있다.
심화 과제
- 이 앱에 적합한 테스트 전략을 만들었나요?
각 팀원들의 테스트 전략은?
김유현 과제로 주어진 어플리케이션의 규모는 비교적 작다고 생각해서 단위 테스트의 비중이 많은 '테스트 피라미드'전략이 적합하다고 생각해요.
한아름 일정 생성·수정·삭제와 같은 기능 중심 페이지에서는 복잡한 비즈니스 로직보다는 사용자의 주요 시나리오 플로우가 핵심입니다. 따라서 테스트 자원은 통합 테스트(E2E 수준의 흐름 테스트)에 우선순위를 두고, 나머지는 최소화하는 트로피 전략(Trophy Strategy)이 적합합니다.
신희원 캘린더 과제에서 새로운 일정 추가/변경/반복설정/삭제 등 요구사항이 계속 변경되기에, 단위 테스트 비중을 높이는 것보단 통합테스트 비중을 높여서 테스트의 신뢰성을 높이는게 맞다고 생각하여 '테스트 트로피' 전략이 적합하다고 생각합니다.
합의된 테스트 전략과 그 이유는 무엇인가요?
합의된 테스트 전략: 테스트 트로피(Trophy Strategy)
1. 애플리케이션 특성에 적합
캘린더 애플리케이션은 일정 생성, 수정, 삭제와 같은 사용자 중심의 기능 플로우가 핵심 복잡한 비즈니스 로직보다는 사용자 시나리오의 원활한 동작이 더 중요
2. 요구사항 변경에 대한 대응력
새로운 일정 추가, 변경, 반복 설정, 삭제 등 요구사항이 지속적으로 변경되는 환경 통합 테스트는 개별 컴포넌트의 변경보다는 전체적인 사용자 플로우의 안정성을 보장
3. 테스트 신뢰성 확보
단위 테스트만으로는 컴포넌트 간 상호작용에서 발생할 수 있는 문제를 놓칠 수 있음 통합 테스트 중심으로 실제 사용자가 경험하는 시나리오를 검증하여 더 높은 신뢰성 확보
통합 테스트 (Integration Tests): 최대 비중 - 사용자 플로우 중심의 E2E 수준 테스트 단위 테스트 (Unit Tests): 중간 비중 - 핵심 로직에 대한 최소한의 테스트 E2E 테스트: 최소 비중 - 주요 사용자 시나리오에 대한 종단간 테스트
추가로 작성된 테스트 코드는 어떤 것들이 있나요?
1. 종료일을 지정하지 않았을 때 반복 시작일이 10월 30일 이후이면 시작 날짜에 대한 일정 하나만 생성 (hooks)
경계값 테스트의 중요성: 10월 30일이라는 특정 날짜 기준의 비즈니스 로직을 검증 예외 상황 처리: 반복 일정 생성 시 발생할 수 있는 특수한 케이스 대응 데이터 무결성 보장: 잘못된 반복 일정 생성으로 인한 데이터 오류 방지
2. 반복 생성된 일정들과 이미 존재하는 일정이 겹칠 때 경고 표시 (integration)
사용자 경험(UX) 개선: 일정 충돌 상황을 사용자에게 명확히 알려 혼란 방지 실제 사용 시나리오 검증: 반복 일정과 기존 일정 간의 상호작용을 통합적으로 테스트 비즈니스 로직 검증: 일정 충돌 감지 및 경고 시스템의 정확한 동작 확인
3. 반복 일정을 추가할 때 반복 유형이 선택되지 않았을 경우 폼 유효성 검사 (integration)
사용자 입력 검증: 필수 입력값 누락 시 적절한 피드백 제공 오류 방지: 불완전한 데이터로 인한 시스템 오류 사전 차단 사용자 가이드: 올바른 입력 방법을 사용자에게 안내하여 사용성 향상
추가된 테스트 코드를 코멘트로 표시했습니다!
과제 셀프회고
기술적 성장
TDD 방식으로 과제를 진행하며 테스트 중심 설계와 상태 관리 로직을 직접 다뤄보면서 반복, 단일 일정의 crud, 겹침 경고 표 등 실제 사용자 흐름에 맞춘 테스트 설계 능력이 향상된 것 같다. 테스트 코드를 먼저 작성하면서, 현재 플로우에서 개선이 필요한 부분이 무엇인지 사용자 경험상 취약한 부분이 무엇인지 더 명확히 파악할 수 있었다.
코드 품질
// 기존
const [isRepeating, setIsRepeating] = useState(initialEvent?.repeat.type !== 'none');
// 수정
const [isRepeating, setIsRepeating] = useState(
initialEvent ? initialEvent.repeat.type !== 'none' : false
);
- 기존 useEventForm 훅 내의 isRepeating 초기값이 (initialEvent 인자가 없을 경우) true 로 설정되는 문제가 있었습니다. 위의 코드처럼 초기 값을 false로 하여 처음 화면을 렌더링했을 때 반복 일정 체크박스가 선택되어있지 않도록 개선하였습니다.
// src/utils/repeatUtils.ts
// 반복 일정을 단일 일정으로 변환
export const toSingleEvent = (event: Event | EventForm): Event | EventForm => ({
...event,
repeat: { type: 'none', interval: 0 },
});
// 반복 시작 날짜가 반복 종료 날짜보다 앞인지 확인
export const checkEndDateValid = (start: Date, end: Date): boolean => {
return start <= end;
};
// ...
export const getRepeatEventList = (event: EventForm): EventForm[] => {
if (event.repeat.type === 'none') return [event];
switch (event.repeat.type) {
case 'daily':
return getDailyRepeatEvents(event);
case 'weekly':
return getWeeklyRepeatEvents(event);
case 'monthly':
return getMonthlyRepeatEvents(event);
case 'yearly':
return getYearlyRepeatEvents(event);
default:
return [event];
}
};
- 반복 생성된 일정을 수정할 경우 단일 일정으로 수정되어야 하기 때문에 반복 일정을 단일 일정으로 수정하는 유틸 함수를 작성했습니다.
- 반복 종료일을 지정하지 않은 상태에서 반복 일정을 2025-10-30 이후부터 생성할 경우 해당 일정 하나만 생성되도록 구현하기 위해 반복 시작 날짜가 반복 종료 날짜보다 앞인지 확인하는 검증 함수를 추가했습니다.
- 반복 유형에 따라 매일, 매주, 매월, 매년 반복 일정을 생성하는 함수를 각각 작성하였고,
getRepeatEventList함수 내에서 각 케이스에 맞게 호출되도록 하여 가독성을 높였습니다.
학습 효과 분석
과제의 주제가 TDD이기 때문에 실제로 테스트 코드 작성 -> 기능 구현 -> 테스트 통과시키기 순으로 과제를 진행했는데, 테스트 코드 자체에 오류가 있는 경우가 있어 결국 테스트 코드를 수정하는 일이 많았다.. 특히 dom 요소를 선택하고 사용자 이벤트를 연결하는 과정이 생각보다 쉽지 않았다. (누락된 label, htmlFor 값 추가 등) 이번 과제를 통해 요소를 정확히 찾아 이벤트를 트리거하는 과정을 거치면서 DOM 조작과 이벤트 시뮬레이션에 대한 이해가 크게 향상되었고, 테스트 코드 작성 시 사용자 행동을 실제와 가깝게 재현하는 방법에 대한 감각도 익힐 수 있었다.
과제 피드백
리뷰 받고 싶은 내용
현재 신입 개발자 취업을 준비하고 있는데요. 항해 부트캠프를 통해 배운 것들(spa, 리액트 구현, fsd 아키텍처, 테스트 코드 등)을 이력서나 포트폴리오에 녹여낼 수 있는 방법이 궁금합니다. 특히 테스트 코드같은 경우는 실제 협업에서 활용 빈도나 우선순위가 높지 않다고 하는데, 개인 프로젝트에서 테스트 코드를 작성한 경험이 있는 게 신입 입장에서 큰 장점이 될 수 있을까요??
과제 피드백
수고했습니다. 이번 과제는 TDD 방법론을 실제로 경험해보면서 테스트 주도 개발의 Red-Green-Refactor 사이클을 체험하는데 목적이 있었습니다. 팀에서 테스트 전략을 논의하고 트로피 전략에 합의한 과정이 체계적이었어요.
"반복 생성된 일정들과 이미 존재하는 일정이 겹칠 때 경고 표시" 같은 테스트는 실제 사용자가 마주할 수 있는 상황을 잘 고려한 것 같네요. isRepeating 초기값 문제 수정이나 반복 유틸리티 함수들을 케이스별로 분리한 점도 좋았습니다.
"테스트 코드 자체에 오류가 있어 결국 테스트 코드를 수정하는 일이 많았다"는 경험이 사실 TDD의 핵심 학습 포인트예요. 완벽한 테스트를 처음부터 작성하는 게 아니라, 테스트와 구현을 반복적으로 개선해가는 과정이거든요.
Q) 현재 신입 개발자 취업을 준비하고 있는데요. 항해 부트캠프를 통해 배운 것들(spa, 리액트 구현, fsd 아키텍처, 테스트 코드 등)을 이력서나 포트폴리오에 녹여낼 수 있는 방법이 궁금합니다. 특히 테스트 코드같은 경우는 실제 협업에서 활용 빈도나 우선순위가 높지 않다고 하는데, 개인 프로젝트에서 테스트 코드를 작성한 경험이 있는 게 신입 입장에서 큰 장점이 될 수 있을까요??
=> 테스트 코드 경험이 신입에게는 분명 큰 장점이 됩니다. 실제로 현업에서 테스트 코드 작성 빈도가 높지 않은 것이 사실이지만, 바로 그렇기 때문에 더 차별화 포인트가 되겠지요. 서류에는 몰라도 면접에서는 궁금해서 어떤 식으로 했는지 물어볼게요. 대부분의 개발자들이 테스트 코드를 깊게 다루지는 않으니까요.
=> 그리고 대부분 궁금한건 할 줄 아느냐? 혹은 배웠느냐? 해봤습니다. 정도의 대답이면 그렇게 흥미로워 하지 않습니다. TDD가 현업에서 쓰기 힘든건 어렵기 때문이 아니거든요. 해봐서 알겠지만 분명 도움도 되고 좋다는 것도 알지만 그걸 꾸준히 하는데에는 많은 관리와 에너지가 들기 때문인거죠.
=> 그렇기에 실제로 효용체감을 하는 경험 그리고 충분한 기간과 활용담이 뒷받침이 된다면 얼마든지 어필을 할 수 있습니다. 테스트 코드를 작성할 줄 안다가 아니라 프로젝트를 하면서 실제로 테스트를 어떻게 사용했다는 그 경험이 중요합니다. 면접관이 궁금한건 경험과 그로 인한 본인의 철학과 태도니까요.
=> 한번 생각해보세요. 항해를 한 모든 친구들이 테스트 코드를 작성했고 TDD를 해봤지만 테스트 코드에 대해서 오프코치님에게 물어보거나 - 심지어 저도차도 - 궁금해하는건 업무에서 실제로 어디까지 어떻게 써봤는지에 대한 경험이니까요. 그건 정말로 높게 살수 있답니다.
=> 그밖에 부트캠프를 통해 배운 것들(spa, 리액트 구현, fsd 아키텍처, 테스트 코드 등) 자체를 어필하기에는 그런 사람들은 너무 많아요. 이를 어떻게 응용을 했는나 경험을 했는가이죠. 이런 걸 배우게 되면 코드에서 중요하게 여기는 관점을 바꾸게 해주겠지만 실제로 달리는 건 본인이 해야 하니까요.
지난 8주간 배웠던 경험들을 실제로 본인의 것으로 소화하고 만들어가는 과정을 통해 취업 활동에서 분명 큰 도움이 될 거예요. 다음 과제도 화이팅입니다! 수고하셨습니다.
OFF: 안녕하세요! 오프입니다. 제가 결과를 수정했는데요. 심화과제를 불합격 시킨 이유는 전에 공지에 공유드렸듯이 e2e, 시각적 회귀테스트가 없다는 맥락이였습니다. 아쉽지만 참고 부탁드립니다ㅠㅠ