2023-04-25-TIL
2023-04-25-TIL
Today I Learned
REST API 설계 모범 사례
여러 API 가이드라인을 모아 보면 공통으로 나오는 원칙이 있다.
- URI는 동작이 아니라 리소스를 나타낸다. Microsoft의 API 설계 가이드는 리소스 이름에 명사를 쓰고 URI에 동사를 넣지 말라고 권한다(
POST /orders이지/createOrder가 아니다). - 동작은 HTTP 메서드로, 결과는 상태 코드로 표현한다. GET은 안전하고, PUT과 DELETE는 멱등해야 한다.
- 이름 규칙을 하나로 정해 지킨다. Zalando 가이드라인은 리소스 이름을 복수형으로, 경로 세그먼트는 kebab-case로, 쿼리 파라미터는 snake_case로 쓰라고 정한다. 규칙 자체보다 API 전체에서 일관된 것이 중요하다.
- 컬렉션 조회에는 페이지네이션, 필터링, 정렬을 처음부터 넣고, 오류 응답 형식을 통일한다.
- 하위 호환이 깨지는 변경에 대비해 버전 관리 전략을 미리 정한다.
Zalando와 SBB(스위스 연방 철도)의 가이드라인은 회사 내부 규칙을 공개한 문서라 실제 팀이 어떤 수준까지 규칙을 정하는지 참고하기 좋다.
- https://hevodata.com/learn/rest-api-best-practices/
- https://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api
- https://schweizerischebundesbahnen.github.io/api-principles/restful/best-practices/
- https://learn.microsoft.com/en-us/azure/architecture/best-practices/api-design
- https://www.techtarget.com/searchapparchitecture/tip/16-REST-API-design-best-practices-and-guidelines
- https://techbeacon.com/app-dev-testing/guide-restful-api-design-35-must-reads
- https://opensource.zalando.com/restful-api-guidelines/
- https://stackoverflow.blog/2020/03/02/best-practices-for-rest-api-design/
Combo box vs Drop down
화면 요구사항을 이야기할 때 두 용어가 섞여 쓰여서 차이를 정리했다. 드롭다운은 버튼을 누르면 목록이 펼쳐지고 그중 하나를 고르는 컨트롤로, HTML의 <select>가 대표적이다. 콤보 박스는 입력 필드와 팝업 목록을 결합한 컨트롤이다. W3C의 ARIA Authoring Practices Guide는 combobox를 텍스트 입력을 지원하는지에 따라 나눈다. 입력 없이 팝업에서 고르기만 하는 것은 select-only, 입력이 가능한 것은 editable combobox다. editable combobox는 임의의 값을 받을 수도 있고, 입력을 허용된 값의 필터로만 쓸 수도 있다.
정리하면 선택지가 적고 고정되어 있으면 드롭다운, 선택지가 많아서 타이핑으로 걸러야 하거나 목록에 없는 값도 받아야 하면 콤보 박스가 맞다. API 쪽에서는 콤보 박스의 자동 완성을 위해 검색어로 후보를 걸러 주는 조회 API가 필요해진다.
MapStruct
MapStruct는 매퍼 인터페이스를 선언하면 컴파일 시점에 매핑 구현체를 생성하는 애너테이션 프로세서다. 실무에서 자주 쓰는 설정은 다음과 같다.
@Mapper(componentModel = "spring"): 생성된 구현체를 스프링 빈으로 등록해 주입받는다.@Mapping(target = "...", source = "..."): 이름이 다른 필드를 연결하고,ignore = true로 특정 필드를 제외한다.unmappedTargetPolicy: 대상 객체에 값이 채워지지 않은 필드가 있을 때의 정책이다. 레퍼런스에 따르면ERROR는 코드 생성을 실패시키고,WARN은 빌드 경고를,IGNORE는 무시한다. 엄격하게 하려면ERROR로 두고 필요한 필드만 명시적으로 ignore하는 편이 매핑 누락을 빨리 잡는다.
Lombok과 함께 쓸 때는 Lombok이 getter/setter를 먼저 만들어야 MapStruct가 인식하므로 애너테이션 프로세서 순서(lombok-mapstruct-binding)를 맞춰야 한다는 점이 여러 글에서 반복된다.
- https://medium.com/naver-cloud-platform/%EA%B8%B0%EC%88%A0-%EC%BB%A8%ED%85%90%EC%B8%A0-%EB%AC%B8%EC%9E%90-%EC%95%8C%EB%A6%BC-%EB%B0%9C%EC%86%A1-%EC%84%9C%EB%B9%84%EC%8A%A4-sens%EC%9D%98-mapstruct-%EC%A0%81%EC%9A%A9%EA%B8%B0-8fd2bc2bc33b
- https://dev-alxndr.tistory.com/38
- https://itsowavy.oopy.io/spring/mastruct-mapping-1
- https://stir.tistory.com/286
- https://recordsoflife.tistory.com/781
- https://github.com/eugenp/tutorials/tree/master/mapstruct
- https://www.baeldung.com/mapstruct
- https://www.baeldung.com/mapstruct-ignore-unmapped-properties
- https://stackabuse.com/guide-to-mapstruct-in-java-advanced-mapping-library/
- https://www.tutorialspoint.com/mapstruct/index.htm
- MapStruct Reference Guide
MapStruct로 Page 객체 변환
Page<Entity>를 Page<Dto>로 바꾸려고 MapStruct에 Page 매핑 메서드를 만들 필요는 없다. Spring Data의 Page에는 map(Function) 메서드가 있어서 page.map(mapper::toDto)처럼 내용만 변환하고 페이지 번호, 크기, 전체 개수 같은 메타데이터는 그대로 유지한다. MapStruct에는 단건 변환 메서드만 두면 된다.
- https://okky.kr/questions/1333309
Spring에서 트랜잭션 격리 수준 지정하기
Spring은 @Transactional(isolation = Isolation.READ_COMMITTED)처럼 트랜잭션마다 격리 수준을 지정하게 해 준다. Spring 문서에 따르면 기본값은 ISOLATION_DEFAULT로, Spring이 따로 정하지 않고 데이터베이스의 기본 격리 수준을 따른다(MySQL InnoDB라면 REPEATABLE READ). 또한 isolation 속성은 새 트랜잭션을 시작하는 전파 속성인 REQUIRED나 REQUIRES_NEW에서만 적용된다. 이미 진행 중인 트랜잭션에 참여하는 메서드에 다른 격리 수준을 적어도 바깥 트랜잭션의 설정이 그대로 쓰인다는 뜻이다.
격리 수준은 결국 JDBC Connection.setTransactionIsolation()으로 전달되므로, 실제 동작은 데이터베이스가 그 수준을 어떻게 구현하는지에 달려 있다.
- https://www.baeldung.com/spring-transactional-propagation-isolation
- https://docs.spring.io/spring-framework/docs/4.1.5.RELEASE/spring-framework-reference/html/transaction.html
- https://stackoverflow.com/questions/8490852/spring-transactional-isolation-propagation
- Using @Transactional (Spring Framework Reference)
댓글
아직 댓글이 없습니다