일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- 레벨2
- AWS
- 우아한테크코스
- 스프링 부트
- 트랜잭션
- 미션
- 프로그래머스
- 코드리뷰
- Docker
- Paging
- 의존성
- 자바
- JPA
- 서블릿
- 프리코스
- Spring Batch
- HTTP
- 세션
- AOP
- CircuitBreaker
- REDIS
- Level2
- MSA
- 스프링부트
- 백준
- mock
- JUnit5
- 우아한세미나
- 우테코
- yml
- Today
- Total
늘
레벨2 - 지하철 노선도 본문
1. Transactional(readOnly = true)효과
1.성능상에 이점
readOnly일 경우 트랜잭션 ID를 부여하지 않아 조금의 성능 향상이 있다.
2.안전성
읽기가 아닌 수정하는 동작이 발생하면 TransientDataAccessResourceException 예외를 발생하여 변경을 막아줄 수 있습니다.
- 그런데 디비에 따라 다르고 Mysql은 위처럼 발생하지만 h2 인메모리 디비에서는 예외가 발생하지 않는다고 한다.
3. 가독성
readOnly가 붙은 메서드는 모두 조회 메서드라는 것을 한 눈에 파악할수 있다는게 장점이다. 예를 들어 프로그램이 커지면 수많은 메서드들이 존재할텐데 그중에서 조회 로직을 수정해야한다면, readOnly가 달린 메서드들만 빠르게 찾아 수정할수 있다는 장점이 있습니다.
4. R, CUD분리
추후에 DB 이중화가 이루어지면 readOnly가 붙은 메서드들은 조회용 디비로, 아닌 메서드들은 CUD용 디비를 이용한다.
2. assertAll() 테스트 리펙토링
// before
assertAll(
() -> assertThat(sections.getSections()).hasSize(1),
() -> assertThat(section.getUpStation()).isEqualTo(topStation),
() -> assertThat(section.getDownStation()).isEqualTo(bottomStation),
() -> assertThat(section.getDistance()).isEqualTo(7),
() -> assertThat(section.getId()).isEqualTo(topSection.getId())
);
// after ver1.0
assertThat(sections.getSections()).hasSize(1)
.extracting("id", "upStation", "downStation", "distance")
.containsExactly(
tuple(topSection.getId(), topStation, bottomStation, 7)
);
//ver2.0
assertThat(sections.getSections()).hasSize(1)
.extracting(Section::getId, Section::getUpStation, Section::getDownStation, Section::getDistance)
.containsExactly(
tuple(topSection.getId(), topStation, bottomStation, 7)
);
3. 커스텀 예외
비즈니스 로직과 관련된 에러는 RuntimeException을 상속받는 커스텀 예외를 만들어서 처리하고 있어요 :)
RuntimeException <- BusinessException <- EntityNotFoundException 과 같이요.
핸들러에서는 BusinessException를 일괄로 처리하면서 인자로 받은 예외객체에서 메세지를 꺼내 사용자에게 보내줍니다. 그 외 인지하지 못한 부분에서 발생하는 에러는 Exception 핸들러를 정의하고, “알 수 없는 오류가 발생했습니다. 잠시후 다시 시도해주세요”와 같은 메세지를 보내주고 있어요. 만약 예외에서 바로 메세지를 꺼내게되면 시스템/테이블 구조가 너무 잘 드러나서 위험하기 때문이에요. (ex. SqlException의 경우 메세지에 담긴 쿼리로 테이블 구조를 추측할 수 있고, 악의적인 사용자가 데이터를 모두 날리는 쿼리를 요청값에 넣을 수도 있음 - SQL Injection)
4. Exists 로 속도 개선
boolean existByName()
데이터양이 극히 적은 상황에서 WHERE 절로 값을 뽑아내고 비교해서 boolean 상황을 판단하는데 3초에서 4초 정도가 나온다.
이렇게 적은 데이터에서도 속도가 더 빠르다! exists 할땐, limit 1 을 사용하자
하지만 JPA에서는 알아서 limit1로 최적화를 해준다고 한다. JPA쓸때는 따로 쿼리를 수정할 필요가 없겠다!
5. Controller에서 도메인 노출
https://github.com/woowacourse/atdd-subway-map/pull/277#discussion_r871861663
도메인 객체를 컨트롤러에 노출할 경우 컨트롤러에서 의도치 않은 도메인 로직을 수행할 가능성이 생깁니다.
컨트롤러에서 도메인 객체를 변경함에 따라 발생하는 사이드 이펙트의 추적이 어렵습니다.
비즈니스 로직은 서비스 레이어에서 하나의 트랜잭션으로 수행 되어 응답하는데, 컨트롤러에서 추가적인 로직으로 인해 응답값이 변경되는 사이드 이펙트가 발생할 수 있습니다.
컨트롤러는 그 결과값을 뷰에 응답 해주는 로직만 가지는게 본래 목적과 맞다고 생각합니다.
'우아한테크코스 4기 > 코드리뷰' 카테고리의 다른 글
레벨2 - 장바구니 (0) | 2022.06.27 |
---|---|
[레벨2] 지하철 경로 (0) | 2022.05.28 |
레벨2 - 체스 (0) | 2022.05.02 |
[4주차] 체스 - level1 마지막 미션 (0) | 2022.04.08 |
[3주차] 블랙잭 (0) | 2022.03.18 |