- 블로그 검색
- 키워드를 통해 블로그를 검색할 수 있습니다.
- 검색 결과에서 Sorting(정확도순, 최신순) 기능을 지원합니다.
- 검색 결과는 Pagination 형태로 제공합니다.
- 인기 검색어 목록
- 사용자들이 많이 검색한 순서대로, 최대 10개의 검색 키워드를 제공합니다.
- Java 17
- Spring Boot 2.7
- Gradle 7.6
- JPA
- QueryDSL
Dependency | Version | Purpose |
---|---|---|
io.github.resilience4j:resilience4j-all | 2.0.2 | 서킷브레이커 기능을 제공하는 라이브러리 |
com.h2database:h2 | 2.1.214 | 테스트용 인메모리 데이터베이스 라이브러리 |
org.apache.httpcomponents:httpclient | 4.5.14 | HTTP 클라이언트 기능을 제공하는 라이브러리 |
it.ozimov:embedded-redis | 0.7.2 | 테스트용 내장 Redis 인스턴스를 제공하는 라이브러리 |
com.querydsl:querydsl-jpa | 5.0.0 | JPA를 사용하여 타입 안전한 쿼리를 작성할 수 있게 도와주는 라이브러리 |
com.querydsl:querydsl-apt | 5.0.0 | Querydsl을 사용하기 위한 애노테이션 프로세서 라이브러리 |
Test Dependency | Version | Purpose |
---|---|---|
com.h2database:h2 | 2.1.214 | 테스트용 인메모리 데이터베이스 라이브러리 |
it.ozimov:embedded-redis | 0.7.2 | 테스트용 내장 Redis 인스턴스를 제공하는 라이브러리 |
- 모듈의 경계 간 분리로 인해 예외를 전파하는 전략으로 판단
- Infrastructure 계층 예외 발생 시 -> Domain 계층의 예외로 포장하여 전파합니다.
- Domain 계층 예외는 Presentation 계층으로 전파 후 ExceptionHandler 에서 사용자에게 예외를 전달하도록 구현하였습니다.
계층마다 예외 발생 시 로그를 남기도록 하였으며, 서버, 외부 API의 장애가 아닌 클라이언트의 오류(검색어 누락) 는 사용자에게 예외를 전달하도록 구현하였습니다.
블로그 검색 이후 키워드를 저장할 때 기존의 키워드의 검색 횟수 update 가 아닌 새로운 키워드 검색 횟수 insert 시점에 동시성 문제가 발생할 수 있다고 판단했습니다.
해당 문제는 조회 -> 저장, 조회 -> 수정
으로 트랜잭션 단위를 설계하고 Redisson 분산 락을 활용하여 동시성 문제를 해결하였습니다.
기본으로 제공하는 카카오 API 를 기준으로 공통된 필드를 추출하고 인터페이스로 추상화하여 확장에 고려한 설계를 하였습니다.
public interface ApiResponseDto {
List<BlogApiPost> getContents();
BlogApiMetadata getMeta();
}
현재 검색기록 저장과 업데이트가 하나의 트랜잭션으로 묶여 분산 락으로 구현되고 있는데, Redis Redisson 장애가 발생 시 서비스 전체에 장애가 전파될 수 있는 구조를 가지고 있습니다. Redis 구성을 이중화 하거나, 동시성 문제를 다른 방법으로 해결할 수 있는지 고려해야 합니다.
GET /api/v1/blog/search
Request
필드명 | 타입 | 설명 | 필수 여부 |
---|---|---|---|
query | String | 검색어 | O |
page | Integer | 페이지 번호 (기본값: 1) | X |
sort | String | 정렬 기준 (기본값: "accuracy") accuracy(정확도순) 또는 recency(최신순) | X |
Response
Message: success
필드명 | 타입 | 설명 |
---|---|---|
documents | array | 블로그 목록 |
meta | object | 메타정보 객체 |
필드명 | 타입 | 설명 |
---|---|---|
blogName | string | 블로그 이름 |
contents | string | 블로그 내용 |
datetime | string | 작성 일시 |
thumbnail | string | 블로그 썸네일 이미지 URL |
title | string | 블로그 제목 |
url | string | 블로그 URL |
필드명 | 타입 | 설명 |
---|---|---|
isEnd | boolean | 마지막 페이지 여부 (true/false) |
pageableCount | integer | total_count 중 노출 가능 문서 수 |
totalCount | integer | 검색 결과 문서 총 개수 |
GET /api/v1/popular/search
Response
Message: success
필드명 | 타입 | 설명 |
---|---|---|
keyword | String | 검색어 |
count | int | 조회수 |
GET /actuator/circuitbreakers
Response
필드명 | 설명 |
---|---|
failureRate | 실패율 |
slowCallRate | 느린 요청 비율 |
failureRateThreshold | 실패율 임계값 |
slowCallRateThreshold | 느린 요청 비율 임계값 |
bufferedCalls | 버퍼링된 요청 수 |
failedCalls | 실패한 요청 수 |
slowCalls | 느린 요청 수 |
slowFailedCalls | 느린 요청 중 실패한 요청 수 |
notPermittedCalls | 차단된 요청 수 |
state | 서킷 브레이커의 상태 (OPEN, CLOSED, HALF_OPEN) |
GET /actuator/health
Response
Component | Status | Details |
---|---|---|
circuitBreakers | UNKNOWN | |
db | UP | database: H2, validationQuery: isValid() |
diskSpace | UP | total: 0, free: 0, threshold: 0, exists: true |
ping | UP | |
redis | UP | version: 2.8.19 |
https://github.com/minsoozz/blog-searcher-java-springboot/blob/main/lib
java -jar blog-searcher-1.0.0.jar
- 애플리케이션, 레디스 포트가 이미 실행중인 경우 프로젝트가 정상적으로 실행되지 않을 수 있습니다.