본문 바로가기
Memo/우테코 4기

[우테코] 지하철 노선도 미션 1~2 단계 학습 로그

by 연로그 2022. 5. 18.
반응형

목차

1. GitHub 저장소

2. 요구사항

3. 공부한 개념들

4. 피드백

5. 셀프 회고


 


1. GitHub 저장소 🐱‍💻

 

1, 2단계 Repository: https://github.com/yeon-06/atdd-subway-map/tree/step1

1, 2단계 Pull Request: https://github.com/woowacourse/atdd-subway-map/pull/190

3단계 Repository: https://github.com/yeon-06/atdd-subway-map/tree/step2

3단계 Pull Request: https://github.com/woowacourse/atdd-subway-map/pull/274

 


2. 요구사항 🚀

 

img: Pixabay

 

🔻 1단계 요구사항

더보기

데이터 형식은 API 문서 에 따른다.

  1. 지하철 역 관리 API 기능 완성
    • 지하철 역 생성 시 이미 등록된 이름으로 요청한다면 에러 응답

  2. 지하철 노선 관리 API 구현
    • 지하철 노선 등록
      • (e) 등록된 이름으로 요청
    • 지하철 노선 목록
    • 지하철 노선 조회
      • (e) 조회된 노선이 없는 경우
    • 지하철 노선 수정
      • (e) 중복된 이름으로 수정
    • 지하철 노선 삭제

  3. End to End 테스트 작성

 

🔻 2단계 요구사항

더보기
  1. 데이터를 H2 DB에 저장하기
    • H2 DB 설정하기
    • 테이블 생성하기
  2. Spring Bean & Spring JDBC 적용하기
    • @Controller, @Service, @Repository 적용
    • 예외 @ExceptionHandler로 처리
    • JdbcTemplate는 NamedJdbcTemplate 사용

 

🔻 3단계 요구사항

더보기

제공된 페이지 를 통해 테스트할 수 있다.
데이터 형식은 변경된 API 문서 에 따른다.

  1. 지하철 노선 API 변경
    • 지하철 노선은 구간 정보를 포함
    • 노선 등록 시 upStationId, downStationId, distance 데이터 추가 입력
      • (e) 존재하지 않는 역 이용
      • (e) 상행과 하행 역이 동일
      • (e) 거리가 0 이하
    • 노선 조회 시 구간 정보를 포함

  2. 구간 API 구현
    • 구간 등록
      • (e) 거리가 0 이하
      • (e) 새로운 역과의 거리가 기존 역과의 거리보다 크거나 같은 경우
      • (e) 이미 등록된 구간
      • (e) 갈래길 (ex: A역의 상행으로는 B역, C역 두 갈래가 있다)
      • (e) 존재하지 않는 역 이용
      • (e) 상행과 하행 역이 동일
    • 구간 제거
      • 중간에 역 제거 시 구간 재배치 필요 (거리는 두 구간의 합으로 설정)

 


3. 공부한 개념들 📚

 

3-1. Spring의 Transaction 관리

  • 트랜잭션이란?
  • Spring의 트랜잭션 관리
  • AOP란?
  • @Transactional이란?

 

👉 https://yeonyeon.tistory.com/223

 

[Spring] Spring의 트랜잭션 관리 (feat: @Transactional)

📑 목차 트랜잭션이란? 트랜잭션 예제 트랜잭션 ACID 트랜잭션 관리의 종류 비즈니스 로직과 트랜잭션 코드의 분리 선언적 트랜잭션 관리 vs 프로그래밍적 트랜잭션 관리 @Transactional이란? AOP @Tran

yeonyeon.tistory.com

 

 

3-2. 파라미터를 Optional로 받지 말자

👉 https://yeonyeon.tistory.com/224

 

[Java] 파라미터를 Optional로 받지 말자

'Optional ' used as type for parameter '파라미터명' 경고 어쩌다보니 Optional 파라미터를 받는 메서드를 만들게 되었다. 노란줄이 쳐지며 경고가 떴다. 대체 왜? 🤔 private void test(Optional id) { // .....

yeonyeon.tistory.com

 

 

3-3. Domain vs Entity

  • Domain: 소프트웨터로 해결하고자 하는 문제 영역
  • Entity: 정보를 저장할 수 있는 개념 (식별자를 가짐)

 

 

3-4. 의존 관계 주입하기

미션 1단계를 통해 의존 관계를 ^직접^ 주입하며 크고 작은 에러를 많이 만났습니다. 그러다 테스트 코드를 위한 메서드를 생성하는 상황(..)까지 이르렀는데 @Component를 활용한 후로 훨씬 편해졌어요. 컴포넌트 어노테이션을 왜 사용하느냐 라고 누가 묻는다면 그 이유 중 하나로 편리한 의존 관계 주입이라고 답할 것 같아요!👍

 

 

3-5. Repository vs Dao

  • Repository: 객체의 상태를 관리하는 저장소.
  • Dao: 데이터 영속성의 추상화. 주로 DB에 접근하기 위해 사용.

 

👉 결론적으로 Repostiory를 적용하지는 않았다. 다만 리뷰어가 Domain Service라는 새로운 개념을 가르쳐주셨는데 좀 솔깃해서 이 부분에 대해서 공부해봐야겠다.

 

 

3-6. @Transactional의 readOnly

이는 단지 '힌트'일 뿐이고 실제 DB를 read-only 트랜잭션으로 생성할지는 모른다.

JDBC 드라이버가 이에 대한 기능을 구현하는 경우에만 지원이 된다.

 

  • Oracle: 지원
  • MySQL: 5.6.5 부터 지원
  • H2: 미지원

 

 


4. 피드백 💡

🙋‍♀️: 본인(연로그), 👩‍🏫: 리뷰어(또링)

 

4-1. 수정에 대한 상태 코드는 어떤 것이 적절한가?

🙋‍♀️: 저라면 수정된 결과를 응답으로 받아야하면 200, 응답이 불필요하다면 204로 보낼 것 같아요!

 

👩‍🏫: 그렇군요! 좋아요😃 보통 PUT에 대한 응답은 200이나 201로 많이 내려요. 리소스가 생성(혹은 변경)되었다는 의미로 200보다는 조금 더 구체적인 201을 사용하기도합니다!

 

 

4-2. 생성자 체인 사용하기

  • : 생성자에서 다른 생성자를 호출하는 기술
  • 코드의 간소화
  • 유지 관리가 용이
// AS-IS
public Line(String name, String color) {
    this.name = name;
    this.color = color;
}

public Line(Long id, String name, String color) {
    this.id = id;
    this.name = name;
    this.color = color;
}
// TO-BE
public Line(String name, String color) {
    this.name = name;
    this.color = color;
}

public Line(Long id, String name, String color) {
    this(name, color);
    this.id = id;
}

 

 

4-3. 사용자에게 에러 메시지는 보여주지 않아도 될까?

Exception을 처리하기 위해 아래와 같은 코드를 작성했다.

요 부분에서 리뷰어가 한 코멘트를 달아주셨다.

@ExceptionHandler
@ResponseStatus(HttpStatus.BAD_REQUEST)
public String handleException(IllegalArgumentException e) {
    logger.error(EMPTY_STRING, e);
    return e.getMessage();
}

@ExceptionHandler
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public void handleException(Exception e) {
    logger.error(EMPTY_STRING, e);
}

 

👩‍🏫: Exception을 처리하는 경우에 사용자에게 메세지는 내려주지 않아도 괜찮을까요?

 

🙋‍♀️: 코드상 의미있는 메시지를 던져준건 IllegalArgumentException 뿐입니다! 만약 의미있는 메시지를 던지는 Exception이 생기면 그때 가서 별도로 처리해줄 것 같아요~ Exception은 예상치 못한 에러가 발생하는 경우 공통으로 처리해주기 위해 생성한 부분입니다. 헌데 이 '예상치 못한' 메시지들을 사용자에게 던져도 괜찮은가?에 대한 의문이 찜찜해서 따로 넘겨주지는 않았습니다!

 

👩‍🏫: 좋은 관점이에요! 말씀하신대로 서버 예외 메세지를 그대로 클라이언트에 노출할 필요는 없습니다. 위험하기도 하고, 해당 메세지를 보고 사용자는 어떤 액션을 취해야할지몰라 당황스러울테니까요 ㅎㅎ 그래서 현업에서는(= 제가 담당하는 서비스에서는) '예기치 못한 에러에 대해 일시적인 오류가 발생했습니다. 잠시후 다시 시도해주세요.' 와 같은 메세지를 내려줍니다. 그래야 클라이언트도 서버가 어떤 상황인지 알 수 있으니까요. 😃 지금 지하철 프로젝트에서는 크게 신경 안써도 될 것 같으니 참고만 해주세요. ㅎㅎ

 

 

4-4. count보다는 exists

데이터가 존재하는지 검증하기 위해 count(*)를 사용했다. 리뷰어가 jojoldu님 블로그를 주시며 exists를 제안해주셨다. 진짜 exists가 더 빠를지 테스트를 한번 해보았다.

 

h2에 준비해둔 1000개의 데이터
첫 실행 때 차이가 크게 느껴진다. 데이터가 별로 없어서 이후 실행에서는 시간이 비슷했다.

 

 

4-5. null과 size 체크

어떤 리스트를 아래와 같이 검사하는 부분이 있다.

value == null || value.size() == 0

 

리뷰어가 이미 잘 만들어진 util이 있다며 사용을 제안해주셔서 고쳤다.

(내부 코드를 열어보면 위처럼 null과 요소가 존재하는지 체크한다.)

CollectionUtils.isEmpty(value)

 

🔻 CollectionsUtils.isEmpty() 내부 코드

더보기
public static boolean isEmpty(@Nullable Collection<?> collection) {
    return (collection == null || collection.isEmpty());
}

 

 


5. 셀프 회고 🙋‍♀️

 

 요번 미션은 기간이 중간에 늘어났음에도 불구하고 촉박하게 진행한 것 같다. 요구 사항은 다 만족했는데 내 마음에 드는 코드인가? 를 물어보면 그렇지는 못한듯😭 다음 미션은 기존 코드에 이어서 진행해서 리팩터링과 테스트 코드 쪽에 좀 욕심을 내고 싶다. 여러가지 테스트에 대해 공부해보고 다양한 시도를 해보고자 한다😄

 

 슬슬 요구사항 충족하기에 급해서 공부 양이 줄어든 느낌이 든다. 체스 때부터 이랬던 것 같기도 하고... 어떤 개념을 공부하고 코드에 적용하기가 힘든 것 같다. 레벨1까지는 Java만 사용했고 어떤 기능을 만들면 IntelliJ가 더 나은 코드를 추천해주거나 내가 직접 구현해왔다. Spring을 사용하니까 많은 기능들을 지원해주지만 그 기능들을 왜 쓰는지 어떻게 만들어진건지 공부하기에 바쁘다. 설계가 잘 짜여진 코드를 짜고 싶은데 공부할게 많아서 어디부터 건드려야할지..😂 테크 살롱 나와서 크루들 이야기 듣는 것만 해도 고칠 점이 백만개는 생긴다😅 열심히 하자 열심히~!

 

 요번 미션에서 많은 질문을 던져주시며 생각할 여지를 많이 남겨주신 페어 주디와 마지막날 밤까지 게더에서 꼼꼼하게 리뷰해주신 리뷰어 또링 너무 감사해요❤

반응형