Notice
Recent Posts
Recent Comments
Link
«   2025/02   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28
Tags
more
Archives
Today
Total
관리 메뉴

개발계발

[MSA 프로젝트] 각 서비스끼리 통신하기 만들기 본문

프로젝트

[MSA 프로젝트] 각 서비스끼리 통신하기 만들기

Ju_Nik_E 2024. 7. 19. 16:19

MSA 구조 프로젝트를 진행하던 중 각 서비스끼리 통신하는 방법에 대해 기록해놓으려고 한다.

 

WebClient와 FeignClient, 크게 두 가지 방법이 있다.

내가 프로젝트에 적용한 것은 FeignClient이지만, 두 가지 방법 모두 기록해놓자.

 

WebClient

웹클라이언트를 사용하려면 아래 과정을 따라가면 된다.(의존성 파일에 spring-boot-starter-webflux가 추가필요)

 

1. Bean 등록

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.client.WebClient;

@Configuration
public class WebClientConfig {

    @Bean
    public WebClient.Builder webClientBuilder() {
        return WebClient.builder();
    }
}

 

2. 요청 작성(Get, Post요청 예시)

 

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

@Service
public class MyService {

    @Autowired
    private WebClient.Builder webClientBuilder;

    public String getExample() {
        WebClient webClient = webClientBuilder.build();
        Mono<String> response = webClient.get()
            .uri("http://example.com/api/resource")
            .retrieve()
            .bodyToMono(String.class);

        return response.block();
    }
    
    public String postExample() {
        WebClient webClient = webClientBuilder.build();
        MyRequestObject request = new MyRequestObject("data1", "data2");
        Mono<String> response = webClient.post()
            .uri("http://example.com/api/resource")
            .bodyValue(request)
            .retrieve()
            .bodyToMono(String.class);

        return response.block();
    }
}

 

여기까지가 WebClient 사용법인데, 요청의 상세내용을 메서드로 상세하게 작성해줘야하는 번거로움이 있다.

이에 반해 FeignClient는 인터페이스를 이용해 선언적 방식으로 아주 간단하게 요청을 보낼 수가 있다.

 

FeignClient

feignClient를 이용하기위해선 우선 의존성 추가가 필요하다.

    implementation 'org.springframework.cloud:spring-cloud-starter-openfeign:4.1.0'

*버전은 사용중인 스프링부트 버전과 호환되는 지 잘 확인하자

 

 

이후 FeignClient 어노테이션을 이용해 아래와 같이 코드를 작성하면 된다.

package com.jk.module_lecture.client;

import com.jk.module_lecture.common.config.FeignConfig;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

@FeignClient(name = "lectureResourceClient", url = "${feign.lectureResourceClient.url}", configuration = FeignConfig.class)
public interface LectureResourceClient {

    @RequestMapping(method = RequestMethod.POST, value = "/api/v1/internal/lectures/notes", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    void createNote(@RequestPart("file") MultipartFile file, @RequestParam("lectureId") Long lectureId, @RequestParam("title") String title);

    @RequestMapping(method = RequestMethod.POST, value = "/api/v1/internal/lectures/multimedia", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    void createVideo(@RequestPart("file") MultipartFile file, @RequestParam("lectureId") Long lectureId);

    @RequestMapping(method = RequestMethod.GET, value = "/api/v1/internal/lectures/notes/{lectureId}", consumes = "application/json")
    String getNoteUrl(@PathVariable("lectureId") Long noteId);

    @RequestMapping(method = RequestMethod.GET, value = "/api/v1/internal/lectures/multimedia/{lectureId}", consumes = "application/json")
    String getVideoRul(@PathVariable("lectureId") Long videoId);
}

 

위에 작성한 인터페이스를 사용하는 예시 코드는 아래와 같다.

@Slf4j
@Service
@RequiredArgsConstructor

public class LectureService {
	private final LectureRepository lectureRepository;
	private final LectureResourceClient lectureResourceClient;
    
    
    /**
     * 강의 등록
     */
    @Transactional
    public LectureCreateResponseDto create(Long teacherId, LectureCreateRequestDto lectureCreateRequestDto) {
        Lecture lecture = Lecture.builder()
                .title(lectureCreateRequestDto.title())
                .description(lectureCreateRequestDto.description())
                .teacherId(teacherId)
                .price(lectureCreateRequestDto.price())
                .build();

        Lecture savedLecture = lectureRepository.save(lecture);

        lectureResourceClient.createNote(
                lectureCreateRequestDto.file(),
                savedLecture.getLectureId(),
                savedLecture.getTitle()
        );

        lectureResourceClient.createVideo(
                lectureCreateRequestDto.video(),
                savedLecture.getLectureId()
        );

        return LectureCreateResponseDto.builder()
                .lectureId(savedLecture.getLectureId())
                .title(savedLecture.getTitle())
                .description(savedLecture.getDescription())
                .teacherId(savedLecture.getTeacherId())
                .price(savedLecture.getPrice())
                .build();
    }
}

 

위와 같이 서비스 코드에 lectureResourceClient를 주입받아와 간단하게 요청을 보낼 수 있게 된다.

해당 요청을 받는 서비스 모듈은 InternalController api주소를 생성해서 받으면 된다.

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/v1/internal/lectures/notes")
public class InternalLectureNoteController {
    private final LectureNoteService lectureNoteService;
    
    @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public ResponseEntity<Void> createNote(
            @RequestPart(name = "file") MultipartFile file,
            @RequestParam(name = "lectureId") Long lectureId,
            @RequestParam(name = "title") String title
    ) {
        lectureNoteService.create(lectureId, title, file);
        return ResponseEntity.ok().build();
    }
    
}

 

내부적으로 데이터를 주고 받는 것이기 때문에, 요청/응답에 대한 상세내용 또한 외부로 알릴 필요가 없어, WebClient보다 훨씬 간단하게 사용가능한 FeignClient를 사용하자.

'프로젝트' 카테고리의 다른 글

[MSA 프로젝트] AWS S3 연결하기  (0) 2024.07.19