2024-06-12-TIL
2024-06-12-TIL
Today I Learned
검증과 중복검사
검증과 중복검사는 FE에서 처리하는게 좋을까? 아니면 BE에서 처리하는게 좋을까? 검증의 경우에는 FE와 BE에서 둘 다 처리할 수 있는 한 최대한 처리하는게 좋다. 중복검사의 경우에는 DB접근을 통해서 확인해야하는데, 별도의 API를 추가해서 FE로부터 검증된 데이터를 받는게 좋을텐데, BE에서 한 번 더 검증을 할 수도 있다. 경우에 따라 성능이슈 등이 문제가 된다면 FE로부터 검증된 데이터만 넘어온다고 가정하고 그냥 받을수도 있다.
두 계층의 검증은 목적이 다르다. FE 검증은 사용자에게 빠른 피드백을 주기 위한 UX 장치이고, BE 검증은 데이터 무결성과 보안을 위한 최종 방어선이다. 클라이언트 코드는 사용자가 우회하거나 API를 직접 호출할 수 있으므로, 보안이나 정합성에 영향을 주는 규칙은 BE에서 반드시 다시 확인해야 한다.
중복검사도 같다. 아이디 중복 확인 API로 미리 알려 주더라도, 확인과 실제 저장 사이에 다른 요청이 같은 값을 먼저 저장할 수 있다. 그래서 최종적으로는 DB의 unique 제약이 중복을 막고, 애플리케이션은 제약 위반 예외를 받아 적절한 응답으로 바꾸는 구성이 안전하다.
링크의 질문은 같은 검증 규칙을 FE와 BE에 두 번 적는 중복을 어떻게 줄일지를 다룬다. 서버가 검증 규칙을 스키마(JSON Schema 등)로 노출하고 클라이언트가 그것을 읽어 쓰는 방식이 한 가지 선택지다.
- https://softwareengineering.stackexchange.com/questions/431224/eliminating-duplication-of-validation-metadata-in-client-and-server
ResponseEntity Alternative
ResponseEntity는 HTTP 응답 전체(상태 코드, 헤더, 본문)를 표현하는 Spring의 타입이다. 상태 코드와 헤더를 요청마다 다르게 정해야 할 때 유용하지만, 모든 컨트롤러 메서드에서 감싸면 코드가 장황해진다.
대안은 다음과 같다.
- 본문 객체를 그대로 반환한다.
@RestController에서는 기본 상태 200으로 직렬화된다. - 고정된 상태 코드는
@ResponseStatus(HttpStatus.CREATED)로 메서드에 선언한다. - 오류 응답은 예외를 던지고
@RestControllerAdvice의@ExceptionHandler에서 한곳에 모아 상태 코드와 본문을 만든다.
1
2
3
4
5
@PostMapping("/users")
@ResponseStatus(HttpStatus.CREATED)
public UserResponse create(@RequestBody @Valid CreateUserRequest request) {
return userService.create(request);
}
Location 헤더처럼 응답마다 달라지는 헤더가 필요할 때만 ResponseEntity.created(uri).body(...)처럼 빌더를 쓰면 된다.
- https://stackoverflow.com/questions/61138943/what-is-responseentity-for-and-why-should-i-keep-it
- https://medium.com/nerd-for-tech/importance-of-using-responseentity-5e37da704e88
- https://www.baeldung.com/spring-response-entity
- https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/http/ResponseEntity.html
- https://stackoverflow.com/questions/58598047/alternative-response-spring-boot-api-rest
디바운싱과 따닥방지
사용자가 버튼을 빠르게 두 번 누르는 “따닥”은 같은 요청이 두 번 서버로 가서 주문이나 결제가 중복 생성되는 문제를 만든다.
FE에서는 이벤트 호출 빈도를 줄이는 두 가지 기법을 쓴다.
- 디바운싱(debouncing): 연속된 이벤트가 멈춘 뒤 일정 시간이 지나면 마지막 한 번만 실행한다. 검색어 자동완성처럼 입력이 끝난 뒤 한 번 호출하면 되는 경우에 맞다.
- 쓰로틀링(throttling): 일정 시간 간격마다 최대 한 번만 실행한다. 스크롤 이벤트처럼 계속 발생하는 이벤트에 맞다.
버튼 중복 클릭은 요청 중에 버튼을 비활성화하는 것만으로도 상당 부분 막을 수 있다. 하지만 네트워크 재시도나 여러 탭에서 오는 요청까지 FE가 막을 수는 없으므로, BE에서도 방어가 필요하다. 클라이언트가 요청마다 고유한 멱등 키를 보내고 서버가 처리한 키를 일정 시간 기억해 같은 키의 요청을 다시 처리하지 않거나, DB unique 제약이나 분산 락으로 같은 작업의 동시 실행을 막는 방식이 일반적이다.
- https://velog.io/@seoyaon/Javascript-%EB%94%94%EB%B0%94%EC%9A%B4%EC%8B%B1debouncing%EA%B3%BC-%EC%93%B0%EB%A1%9C%ED%8B%80%EB%A7%81throttling
- https://winterroom.tistory.com/168
- https://happysisyphe.tistory.com/72
- https://soobysu.tistory.com/125
Docker ECR
Amazon ECR은 AWS의 컨테이너 이미지 레지스트리다. 이미지를 push하는 순서는 공식 문서 기준으로 다음과 같다.
1
2
3
4
5
6
7
8
9
# 1. 레지스트리 인증
aws ecr get-login-password --region <region> \
| docker login --username AWS --password-stdin <account_id>.dkr.ecr.<region>.amazonaws.com
# 2. 로컬 이미지에 ECR 저장소 주소로 태그
docker tag <image_id> <account_id>.dkr.ecr.<region>.amazonaws.com/<repository>:<tag>
# 3. push
docker push <account_id>.dkr.ecr.<region>.amazonaws.com/<repository>:<tag>
get-login-password로 받은 인증 토큰은 12시간 동안 유효하다. push하기 전에 ECR 저장소가 미리 만들어져 있어야 하고, 태그를 생략하면 latest가 붙는다.
- https://docs.aws.amazon.com/ko_kr/AmazonECR/latest/userguide/docker-push-ecr-image.html
MySQL 8.0 ngram Full-Text Parser
MySQL의 기본 전문 검색 파서는 공백을 단어 구분자로 쓰기 때문에, 띄어쓰기 없이 이어지는 문장이나 조사가 붙는 한국어에서는 원하는 검색이 잘 되지 않는다. ngram 파서는 텍스트를 길이 n의 연속된 문자 조각으로 잘라 색인한다. 문서는 중국어, 일본어, 한국어(CJK)를 위한 파서라고 설명한다.
ngram_token_size는 기본값 2이고 1부터 10까지 설정할 수 있으며, 서버 시작 시에만 바꿀 수 있다. 값이 2라면 abcd는 ab, bc, cd로 쪼개진다.
1
2
3
4
CREATE FULLTEXT INDEX ft_index ON articles (title, body) WITH PARSER ngram;
SELECT * FROM articles
WHERE MATCH (title, body) AGAINST ('검색어' IN BOOLEAN MODE);
검색 모드에 따라 결과가 다르다. Natural language 모드에서는 검색어가 ngram 조각들의 합집합으로 바뀌어 일부 조각만 맞아도 결과에 포함된다. Boolean 모드에서는 조각들을 이어 붙인 구문 검색으로 바뀌어 검색어 전체가 들어 있는 문서만 찾는다. 또 ngram 파서는 불용어를 포함하는 토큰 자체를 제외하고 기본 불용어 목록이 영어 기준이라, 한국어 서비스에서는 불용어 목록을 따로 관리하는 편이 좋다.
- https://dev.mysql.com/doc/refman/8.4/en/fulltext-search-ngram.html
댓글
아직 댓글이 없습니다