기출 오답노트
•
dto 안썼더니 모델이 뷰까지 가게 되어 오히려 코드 짜기 더 번거로웠다. dto 매핑 로직 익숙해져서 가서 금방 짜버리자.
•
설계 1시간 30분했더니 시간이 30분 부족했다. 45분부터 슬슬 컷해서 한시간내에 설계 무조건 끝내자
•
stream 썼다가, 카운트 잘못하는 일 발생했었다. 잘 보고 쓰거나 fot문 그냥 쓰자.
•
function 값 같이 컨트롤러에서만 쓸 enum은 그냥 하드코딩하자. 시간이 부족했었다. exception 메시지도 마찬가지다.
•
불안하겠지만, dto를 출력하는 건 마지막에 하자. 금방한다. 출력해야 할 거 확인해서 dto를 만들긴 하되, dto에 값 넣는 거 먼저 하자. enum까진 dto 안써도 괜찮은듯. 단, enum말고 다른필드도 동시에 필요하다면, 무조건 dto 사용하자.
•
리팩토링보다 기능들이 정상 동작하는 거 전부 확인 완료하는 게 먼저다. 중간, 중간 리팩토링하다가 시간이 부족했었다.
•
enum 끼리 관계 갖을 일 있다면 enum 넣어주자… string 말고.. string 넣었다가 메뉴가 아닌데 메뉴라 생각하고 find함수 돌려서 문제 생겼던 적 있다.
•
주석 일정부분은 그냥 달자. 안달면 오히려 고치는 게 더 오래걸린다. 변수명도 일단은 빠르게 짜야하기에 5초 이상 고민하지 말자.
•
설계 제대로 안하고 해봤더니 오히려 훨씬 오래 걸렸다. 설계 꼭 제대로 집중해서 하자.
12시에 도착해서 할 일
•
노트북 충전기 꽂고 받침대에 고정해두기
◦
노트북에 메일 창, 네이버 영어사전, 노션 창, 내 깃헙 열어두기 (이미 열려있을거임)
•
폰 타이머 켜두기
•
프린트물, 공책, 볼펜, 음료 빼두기
•
시간 남는다면
◦
크리스마스 실행시켜보기 (심적 안정 겸 java17 동작 확인)
◦
중요해서 뽑아온 코드 읽기
너무 떨린다면 명심할 점 → 긴장될 땐 빨리 무언가에 몰입하자! 집중해버리면 긴장이 풀린다.
1.
요구사항을 생각하며 읽고 내 언어로 정리해두자. 물론 한나절 내내 읽으란 게 아니라, 처음에 내 언어로 정리해둬야 나중에 빠트린 거 확인할 때 편하다. 또, 요구사항에서 구조를 유추할 수 있다
2.
설계에 공들이자, 계속 고치는 대공사보다 설계에 공들이는게 더 빠르다
3.
ApplicationTest를 잘 살피고 가능해보인다면 여기 있는 것부터 만들자
4.
라이브러리는 알려준 그대로 쓰자! List를 shuffle 하라 했으면 그 모양 그대로
5.
너무 강박적으로 하지 말자 !! 쫄지 말자!!!!!!!!!!!!!!!!!!!!!!!! 할 수 있다
6.
잘 모르겠을 땐 엄청나게 빠르게 그림 그려보자
7.
다 풀 수 있겠단 생각 드는 순간 어려운 거 나오더라. 절대 그런 생각 하지 말자. 그냥 아무 생각도 하지 말고 문제에 몰입하자. 혹여나 쉽네..?란 생각이 들더라도 그 정도로 쉽게 나올린 없으니 뒤에 나올 내가 빼트린 무언갈 대비해 지금 하고 있는 거 최대한 빨리 끝내자.
8.
다 풀었다면, 리팩토링도 좋지만… 못생긴 코드는 무시하고, 일단 기능들이 다 제대로 동작하는지 확인하자. 특히 outputView쪽은 하드코딩한 건 없는지 확인하자
9.
푸는 도중에 못생긴 코드 일단 TODO 주석만 남겨두고 무시하자. 풀다가 리팩토링 금지
10.
테스트코드는 정말 쓰고 싶겠지만 일단 패스하자 시간 부족하다… 단순히 문법이 좀 헷갈리는 친구 있으면, 차라리 main에서 찍어보자. 나머진 집중력과 정신력으로 코드 작성하며 최대한 이해하기. 또, 커밋 넘길 때 아주 빠르게 스캔하고 넘기며 이해하기.
시간 내에 구현 하기 위해서 포기해도 되는 것 → 다 풀고서 리팩토링할 때 볼 것 순대로 정렬해둠
•
함수 역할, 15라인 신경 X
•
indent 깊이 신경 X
•
출력 시 개행 신경 X
•
상수X(하드코딩O)
•
enum 안 중요해 보이면 과감히 string으로 써버리고 나중에 대체하기
◦
fucntion은 그냥 하드코딩하자. 대신에 dto는 포기하지 말기 (오히려 편함)
•
의존 관계 신경 X(지역변수O)
•
모델 적게 쓰고, 나중에 쪼개기
•
테스트코드 X
•
커밋메시지 괄호 신경 X
1시에 가장 먼저!!!
1.
프로젝트 생성하기
•
포크 방식
◦
팀 깃허브 Fork 하기
◦
내 깃허브 Clone 하기 → ChooSeoyoen 브랜치 만들기
•
템플릿 방식
◦
팀 깃허브 use this template → create a new repository(java-christmas-6-ChooSeoyeon)
▪
private → collaborator → add people → woowa-course 추가
◦
내 깃허브 Clone 하기 → Main 브랜치 그대로 사용하기
2.
프로젝트 열어서 확인하기
•
깃헙 데스크탑에서 initial commit 보내지는지 확인
•
Application, ApplicationTest 부터 돌아가는지부터 확인
3.
Save Actions 적용하기, IntelliJ로 테스트 설정하기, setting이랑 project structure Java 17 확인하기
•
호오옥시 checkStyle 설정 안되어 있다면
◦
Settings > Editor > Code Style > Java > 톱니바퀴 > Import Scheme > Intellij IDEA code style XML > 다운로드 해둔 WootecoStyle 파일 선택해 적용 > OK
◦
Settings > Editor > Code Style > Enable EditorConfig support 설정 활성화
◦
Settings > Tools > Actions on Save > Reformat code, Optimize imports 체크
문서 작업하기 (45분~1시간 안에 무조건 끝내기)
•
docs(README): 요구사항 분석
◦
README
# 기능 구현 목록
## Model
## Constant
## View
### InputView
### OutputView
- [ ] 에러 메시지를 출력한다
## Controller
- [ ] 에러를 관리한다
- [ ] 실행한다
- [ ] 준비한다
- [ ] 시작한다 (입력 받는 거 기준으로 끊기)
- [ ] 종료한다
# 부록
## 기능 요구사항 분석
-
- 예외 처리
## 프로그래밍 요구사항 분석
- 공통
- 클린 : indent 2까지 허용, 3항 연산자 금지, 메서드는 15라인 이하로 한 가지 일, else 금지, switch/case 금지
- 에러 : 에러 메시지 출력 후 다시 입력 받기
- 테스트 : 도메인 로직(UI 제외)에 단위 테스트 구현, 기능 목록 동작을 테스트 코드로 확인
- 분리 : 핵심 로직과 UI 로직 분리, InputView/OutputView 구현
- 라이브러리 : Colsole.readLine() 사용
- 필수 테스트
-
## 메모
- 플로우
- 용어
- 관계
## 스토리텔링식 설계
- 이야기
- 협력 : 요청과 응답
- 책임 : 하는 것(요청, 계산)과 아는 것
- 역할 : 책임의 집합(재사용성)
Java
복사
•
docs(README): 플로우 작성
•
docs(README): 기능 구현 목록 작성
예시
세팅하기
•
chore(package): 초기 패키지와 클래스 추가
◦
model(enums), view, controller
•
feat(Controller): 에러 메시지를 관리한다
◦
Controller
private final InputView inputView;
private final OutputView outputView;
public Controller(InputView inputView, OutputView outputView) {
this.inputView = inputView;
this.outputView = outputView;
}
public void run() {
}
private <T> T repeatUntilSuccessWithReturn(Supplier<T> supplier) {
while (true) {
try {
return supplier.get();
} catch (IllegalArgumentException e) {
outputView.printErrorMessage(e.getMessage());
}
}
}
private void repeatUntilSuccess(Runnable action) {
while (true) {
try {
action.run();
return;
} catch (IllegalArgumentException e) {
outputView.printErrorMessage(e.getMessage());
}
}
}
Java
복사
◦
OutputView
public void printErrorMessage(String message) {
System.out.println("[ERROR] " + message);
}
Java
복사
◦
Application
new Controller(new InputView(), new OutputView()).run();
Java
복사
작업하다 중간 중간 나올 코드
◦
InputView → 왠만하면 parse(+validate), 너무 여러 단계로 쪼개서 parse해야하면 convert로 묶기
// 일반
public String readMoving() {
System.out.println("입력해");
String inputMoving = Console.readLine();
validateInputMoving(inputMoving);
return inputMoving;
}
// List<String> 인데 한 개 이상 무조건 입력
public List<String> readCarNames() {
System.out.println("자동차 이름 입력해");
String inputCarNames = Console.readLine();
return convertInputCarNamesToList(inputCarNames);
}
private List<String> convertInputCarNamesToList(String inputCarNames) {
List<String> carNames = Arrays.stream(inputCarNames.split(","))
.map(String::trim)
.peek(this::validateCarNameNotEmpty)
.toList();
validateCarNamesNotEmpty(carNames);
return carNames;
}
private void validateCarNameNotEmpty(String carName) {
if (carName.isEmpty()) {
throw new IllegalArgumentException("자동차 이름은 앞뒤 공백을 제외한 1자 이상으로 입력해야 합니다.");
}
}
private void validateCarNamesNotEmpty(List<String> carNames) {
if (carNames.isEmpty()) {
throw new IllegalArgumentException("자동차 이름을 하나 이상 입력해야 합니다.");
}
}
// List<String> 인데 null 도 가능 -> https://github.com/ChooSeoyeon/java-menu/blob/ChooSeoyeon/src/main/java/menu/view/InputView.java
public List<String> readMenusBy(String coachName) {
System.out.println("\n" + coachName + "(이)가 못 먹는 메뉴를 입력해 주세요.");
String inputMenus = Console.readLine();
return convertInputMenusToList(inputMenus);
}
private List<String> convertInputMenusToList(String inputMenus) {
try {
return Arrays.stream(inputMenus.split(","))
.collect(Collectors.toList());
} catch (Exception e) {
throw new IllegalArgumentException("메뉴는 쉼표(,)로 구분해 입력해야 합니다.");
}
}
// 정규식 (인데 왠만하면 안쓸거임)
public static void validateMenu(String menu) {
String regex = "([A-Za-z가-힣])+-\\d(,([A-Za-z가-힣])+-\\d+)*"; //문자-숫자 형식은 무조건 와야 하며 ,문자-숫자 형식은 올 수도 있고 안 올 수도 있다는 정규식
if (!Pattern.matches(regex, menu)) {
throw new InvalidFormatException("[ERROR] 유효하지 않은 주문입니다. 다시 입력해 주세요.");
}
}
Java
복사
◦
outputview
public String formatPrice(int number) {
return String.format("%,d", number) + "원";
}
private static final DecimalFormat PRICE_FORMAT = new DecimalFormat("#,###'원'");
PRICE_FORMAT.format(1000);
Java
복사
◦
dto → 매핑 (은 empty 만드는 거 말곤 그냥 모델에서 해버리자,,, dto에서 하면 좋지만 시간 X)
public record EventResult(GiftSummary gift, List<DiscountSummary> discounts, PaymentSummary payment) {
public static EventResult createEmpty() {
return new EventResult(
GiftSummary.createEmpty(),
Collections.emptyList(),
PaymentSummary.createEmpty()
);
}
}
public record PaymentSummary(int totalBenefitPrice, int finalPayment, String badgeName) {
public static PaymentSummary createEmpty() {
return new PaymentSummary(0, 0, BadgeEvent.defaultBadgeName());
}
}
Java
복사
◦
private static String formatGiftBenefit(GiftSummary gift) {
if (gift.price() == 0) {
return "";
}
return Optional.of(gift)
.map(g -> "\n증정 이벤트: -"
+ PRICE_FORMAT.format(g.price()))
.orElse("");
}
public String formatGift(GiftEvent gift) {
if (gift == GiftEvent.NONE) {
return "없음";
}
return gift.getGiftMenu() + " " + gift.getQuantity() + "개";
}
Java
복사
◦
모델 → equals
@Override
public boolean equals(Object obj) {
if (obj instanceof Registration) {
Registration registration = (Registration) obj;
return this.line.equals(registration.line);
}
return false;
}
@Override
public int hashCode() {
return line.hashCode();
}
Java
복사
◦
모델 → crud
▪
save(추가한다), update(수정한다), find(찾는다), delete(제거한다)
▪
exists/check(존재하는지 확인한다), sum(더한다), generate(결과를 만든다), calculate(계산한다)
◦
문법
private final Map<Day, String> weeklyMenus = new LinkedHashMap<>();
Java
복사
잘은 안 나오지만 나오면 당황하지 말자 코드
기능 입력 → 지하철
Test
컨벤션
제출
fork 방식
◦
template 방식
▪
지원 플랫폼 > 내 지원 현황 > ChooSeoyeon 이랑 비공개 저장소 주소 랑 소감 입력