상품 관리 기능을 구현하며, 기술적으로 고민했던 사항들과 배웠던 것들이다.
h2 접속 시 주의할 점
- 기본적으로 파일 db로 동작한다.
- 파일로 바로 연결하는게 아닌 tcp 연결로 변경 해주자.
- 단순하게 db를 확인할때는 상관 없지만, 결국 외부 프로그램에서 동시에 접속해야 하기 때문에 TCP 접속이 가능해야 한다.
- 또한 파일에 직접 접근할 경우에는 lock이 걸려버리니, tcp로 연결하는게 좋다.
코드 작성 방식, 아키텍처 설계 관점
id에 대한 관리 관점
id 값은 일반적으로 외부 시스템에 의해서 주입받는 것이 분산 환경을 위하여 선택하는 적절한 선택이다.
그렇기 때문에, 인프라적 특성을 띄는 해당 id 값을 도메인 레이어 내에서는 알지 못하게 하고 싶은데,
테스트 시에는 해당 id값이 필요한 경우가 많다.
이럴 때, 어떤 방법을 선택해야 하는가에 대한 고찰이다.
RowMapper를 위한 생성자? id값을 넣는 방법
방법 1: DAO가 도메인 엔티티를 직접 사용할 때 - 리플렉션 사용하기
- 일반적으로 새 엔티티 생성용으로 ID를 넣지 않는 생성자를 두고 로우매퍼가 사용할 ID를 넣어야 하는 생성자를 만드는 방법
- BeanPropertySqlParameterSource는 반드시 setter 가 있어야 값 대입이 가능한가?
- 아님.
- BeanPropertySqlParameterSource는 애초에 파라미터 바인딩을 위한 것
- 객체에서 값을 찾아 대입하는 것이기 때문에, getter만 있으면 충분
- BeanPropertyRowMapper는 반드시 setter 가 있어야 값 대입이 가능한가?
- 맞음
- 그래서 그냥 id는 리플렉션으로 넣어주는게 나은듯
- 따로 static 메소드로 빼두는게 좋은듯.
- 근데, 이렇게 되면, 인프라 로직이 컴파일 타임에 잡히지 않는 id 변수 이름에 의존하게 되는 문제가 발생한다.
- 이는 getField 를 써도 똑같을 듯.
- 런타임 어노테이션과 같은 기능과 연동해서, 확실하게 ID임을 밝혀주고 리플렉션으로 넣어주는 기능이 필요하다.
- JPA처럼 외부 스키마 관리 기능이 있으면 참 좋을 것 같다.
- Flyway
- Liquibase
방법 2: 도메인 엔티티와 DAO용 DTO를 분리하기
- 이런 방법도 있겠다 싶지만, 현재 id 1개만 넣어주어야 하는 상황이니, 리플렉션을 활용하는게 더 간편하고 좋아보임.
- 복잡한 엔티티가 아니니, 매퍼를 구현해서 얻는 이득이 없을 것이라 예상됨.
save() 하나로 insert, update 구현 방법
현재 프로잭트는 Spring Data Commons의 AbstractAggregateRoot의 이벤트 기록 기능을, save() 메소드에서 자동으로 발행하기 위해 연습하고 있는 프로젝트이다.
따라서 더티체킹이 아닌 save()로 명시적으로 저장할 수 있어야 하는데, 이때 insert와 update가 별도의 메소드로 분리되어 있으면 "영속화"라는 인프라 책임이 application 레이어에 누수되는 문제가 발생한다.
따라서, insert와 update를 구분하지 않고, save() 메소드로 통일화시키기 위한 방법을 고민한 내용이다.
- id == null이면 insert
- id != null 이면 update
- id 값은 db에서 받아오거나, 프론트에서 받아오거나 하자.
- 서비스 레이어는 id값을 직접 넣지 않도록
- 테스트 시에는 ReflectionTestUtils 로 간편히 id 넣는 생성자를 따로 만드는게 좋은듯.
MapSqlParameterSource.addValue() 사용하기
- Map.of()는 key, value값에 null을 허용하지 않는다.
- 그냥 직접 addValue()로 넣어, 가시성을 높이자.
@JdbcTest
- JDBC 슬라이스 테스트(특정 레이어만을 가볍게 띄워 진행하는 테스트)용 어노테이션이다.
- DDL 생성해두자.
- JDBC 기반이라, Hibernate의 자동 DDL 생성 기능이 없다.
- 따라서 직접 DDL을 테스트 실행 전에 실행시켜줘야 한다.
- 임베디드 DB의 경우, 스프링부트가 resources 폴더의 schema.sql 파일을 자동으로 인식해 실행시킨다.
- 직접 @Import로 빈 등록해야 사용할 수 있다.
- 스프링 부트의 초기화 과정을 진행하지 않기 때문에,
- 직접 import를 통해 빈을 채워야 한다.
HTML, 타임리프
HTML 폼은 GET과 POST만 지원한다.
- 따라서, PUT, DELETE 같은 메소드를 사용하고 싶으면 다음 두가지 설정을 적용해야 한다.
1. 히든 태그 작성
2. HiddenHttpMethodFilter 설정
- 아래와 같은 추가 속성을 설정 파일에 추가해줘야 한다.
- 혹은 다음과 같이 빈 등록을 수행해도 된다.
@Bean
public HiddenHttpMethodFilter hiddenHttpMethodFilter() {
return new HiddenHttpMethodFilter();
}
타임리프는 record 문법을 지원하지 않는다.
- 되도록이면 그냥 자바 빈 프로퍼티 쓰자.
JdbcTemplate 기능
많이 쓰는 기능은 뭐가 있는거지?
- 조회
- queryForObject - 단건 조회
- EmptyResultDataAccessException - 조회되지 않은 경우
- IncorrectResultSizeDataAccessException - 조회한 대상이 2개 이상인 경우
- query - 다수 조회
- List<> 형식으로 반환됨
- queryForObject - 단건 조회
- 수정
- update
- batchUpdate
- SimpleJdbcInsert
- 바인딩
- 조회 - 결과값 바인딩
- RowMapper
- 모든 쿼리 - 쿼리 파라미터 바인딩
- SqlParameterSource
- 조회 - 결과값 바인딩
결과값 바인딩
- RowMapper
- DB->자바용
- Setter 필요 or 생성자 필요
- 따로 static 메소드로 빼두는게 좋은듯.
- select 문 결과값 바인딩 시 사용함.
쿼리 파라미터 바인딩
- SqlParameterSource
- 자바->DB용
- Getter 필요
- 따로 static 메소드로 빼두는게 좋은듯.
- 모든 종류의 쿼리 날릴 때 사용함.
PR 단위에 대한 관점
- 너무 잘게 쪼개니, 지나치게 커밋이 많아지는 기분이 든다.
- 오히려 이것이 버전 단위를 이해하기 어렵게 만드는듯.
- 다음엔 메소드/클래스 레벨이 아닌, E2E feature 단위로 PR을 작성해보자.
'Article > 개인 프로젝트' 카테고리의 다른 글
[Spring JdbcTemplate] JdbcTemplate을 이용한 게시글-댓글 게시판 기능 구현기 (0) | 2025.04.29 |
---|---|
[Spring JdbcTemplate] Spring JdbcTemplate 연습기 (0) | 2025.04.25 |