본문 바로가기
Projects/청하-청년을 위한 커뮤니티 서비스

[청하] 25. 청년 정책 데이터 리프레시 기능 구현

by Lpromotion 2024. 10. 17.

이전 게시글까지 청년 정책 Open API를 연동하고, 데이터베이스에 저장하는 과정에서 스케줄러를 사용해 월요일 자정에 데이터가 리프레시 되도록 설정했다.

스케줄러 이외에도 관리자가 직접 데이터를 리프레시 하는 기능을 추가하여, 임의로 리프레시가 필요하다고 판단될 때 수행할 수 있도록 했다.

 

1. 프로젝트 구조

src/main/java/com/example/withpeace/
│
├── config/                  # 설정 관련 클래스
│   ├── SecurityConfig.java  # Spring Security 설정
│   └── WebMvcConfig.java    # Web MVC 설정
│
├── domain/                 # 도메인 모델 (엔티티)
│   └── YouthPolicy.java    # 청년 정책 엔티티
│
├── repository/                       # 데이터 접근 계층
│   └── YouthPolicyRepository.java    # 게시글 레포지토리
│
├── dto/                                     # 데이터 전송 객체
│   └── response/
│       ├── YouthPolicyListResponseDto.java  #  응답 DTO
│       └── YouthPolicyResponseDto.java      #  응답 DTO
│
├── controller/               # 컨트롤러 계층
│   └── PolicyController.java # 정책 관련 API 엔드포인트
│
└── service/                  # 비즈니스 로직 계층
    └── PolicyService.java    # 정책 관련 비즈니스 로직

 

2. Spring Security 설정

SecurityConfig

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {
    // ... (기존 설정 생략)
    
    @Bean
    protected SecurityFilterChain securityFilterChain(final HttpSecurity httpSecurity) throws Exception {
        return httpSecurity
                // ... (기존 설정 생략)
                .authorizeHttpRequests(requestMatcherRegistry -> requestMatcherRegistry
                        // ... (다른 경로 생략)
                        .requestMatchers("/api/v1/policies/refresh").hasRole(ERole.ADMIN.toString())
                        .anyRequest().authenticated())

                // ... (기존 설정 생략)
    }
}
.requestMatchers("/api/v1/policies/refresh").hasRole(ERole.ADMIN.toString())

Spring Security 설정 파일에서 "/api/v1/policies/refresh" 경로에 `.hasRole(ERole.ADMIN.toString())` 을 통해 관리자(ADMIN)만 리프레시 기능을 사용할 수 있도록 설정했다.

 

3. Web MVC 설정

WebMvcConfig

@Configuration
@EnableWebMvc
@RequiredArgsConstructor
public class WebMvcConfig implements WebMvcConfigurer {
    // ... (기존 코드 생략)

    @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
        configurer
                .defaultContentType(MediaType.APPLICATION_JSON)
                .ignoreAcceptHeader(true)
                .useRegisteredExtensionsOnly(true); // 불필요하므로 제거 예정
    }
}

Spring MVC의 Content Negotiation(컨텐츠 협상)을 구현했다.

응답의 출력 헝식이 XML로 나오는 경우가 있었다. 정확한 이유를 파악하지 못했지만 Open API의 응답 형식이 XML이라서 이것에 영향을 받는 것 같다.

API 응답 형식을 JSON으로 통일하기 위해 `configureContentNegotiation`를 추가했다.

 

configureContentNegotiation 메서드

  • `.defaultContentType(MediaType.APPLICATION_JSON)`
    • 기본 응답 형식을 JSON으로 설정한다.
    • 클라이언트가 특정 형식을 요청하지 않았을 때 JSON 형식으로 응답한다.
  • `.ignoreAcceptHeader(true)`
    • 클라이언트의 Accept 헤더를 무시한다.
    • 클라이언트가 요청한 형식에 관계없이 서버에서 지정한 형식으로 응답한다.
  • `.useRegisteredExtensionsOnly(true)` (제거 예정)
    • 등록된 확장자만 사용하도록 설정한다.
    • URL의 확장자를 통한 컨텐츠 협상을 제한한다.

 

4. 컨트롤러 구현

YouthPolicyController

@RestController
@RequiredArgsConstructor
@RequestMapping("api/v1/policies")
@Slf4j
public class YouthPolicyController {

    private final YouthPolicyService youthPolicyService;

    // 정책 데이터 리프레시 (관리자)
    @PostMapping("/refresh")
    public ResponseDto<?> refreshPolicy(@UserId Long userId) {
        youthPolicyService.scheduledFetchAndSaveYouthPolicy();
        return ResponseDto.ok(true);
    }
}

기존의 스케줄러 메서드(`scheduledFetchAndSaveYouthPolicy`)를 재사용해 리프레시 기능을 구현했다.

 

5. 기존 서비스

YouthPolicyService

@Scheduled(cron = "0 0 0 * * MON") // 매주 월요일 00:00에 실행되도록 설정
@Transactional
public void scheduledFetchAndSaveYouthPolicy() {
    try {
        // 데이터 삭제
        deleteAllYouthPolicies();
        saveCount = 0;

        // 데이터 가져오기 및 저장
        fetchAndSaveYouthPolicy();

        log.info("Youth Policy data update job completed. Total {} policies saved.", saveCount);
    } catch (CommonException e) {
        if (e.getErrorCode() == ErrorCode.YOUTH_POLICY_FETCH_AND_SAVE_ERROR
            || e.getErrorCode() == ErrorCode.YOUTH_POLICY_DELETE_ERROR)
            throw e;
    } catch (Exception e) {
        throw new CommonException(ErrorCode.YOUTH_POLICY_SCHEDULED_ERROR);
    }
}

@Transactional
private void deleteAllYouthPolicies() {
    try {
        youthPolicyRepository.deleteAll();
        log.info("All existing youth policies deleted.");
    } catch (Exception e) {
        throw new CommonException(ErrorCode.YOUTH_POLICY_DELETE_ERROR);
    }
}

`scheduledFetchAndSaveYouthPolicy` 메서드는 기존 정책 데이터를 모두 삭제하고 API를 호출하여 최신 데이터로 갱신하는 작업을 수행한다.

 

6. API 응답 예시

요청 URL

[POST] http://cheongha.site/api/v1/policies/refresh 

 

Response Body

{
  "data": true,
  "error": null
}
반응형

댓글