인프런 영문 브랜드 로고

인프런 커뮤니티 질문&답변

kevin님의 프로필 이미지
kevin

작성한 질문수

실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발

회원 서비스 개발

service와 controller의 역할에 대한 질문이 있습니다.

해결된 질문

작성

·

2.3K

2


=========================================
[질문 템플릿]
1. 강의 내용과 관련된 질문인가요? (예)
2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)
3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예)

[질문 내용]
service와 controller의 역할에 대한 질문이 있습니다.
강의를 듣고 활용해던 도중 의문점이 생겼습니다.
한참 찾아봤는데 아직도 모르겠어서 질문드립니다.
저의 기존 코드에서는 어떤 컨트롤러의 post 요청에서 A,B,C 엔티티가 반드시 순서대로 생성된후 저장되어야 합니다. (참조관계 떄문에 그렇습니다)
기존에 저는 컨트롤러에서 A 엔티티 생성 -> AService.createA() 한 후 B, C도 동일한 과정을 거칩니다.
이러니까 컨트롤러가 서비스의 역할을 해버린다고 생각했습니다.
그래서 컨트롤러는 그냥 dto를 넘겨주고 서비스에서 위의 작업을 하려했습니다.
그랬더니 몇가지 문제가 생겼습니다.
 
1. service에서 repository만을 사용하면 코드 중복이 심함(create 할때 복잡한 중복검증 로직이 있는대, 그것까지 전부 다시 해야함)
2. service에서 service를 주입받아서 하자니 순환참조, 나말고 코드 이해도 낮은 다른사람이 손대면 실수할 가능성 높음 등등의 문제가 생김 그래서 그냥 원래대로 냅두려니 controller가 너무 크고 service가 하는게 그냥 repository로 요청 보내기인 경우가 대부분임
그래서 질문은
1. 적절한 방법이 뭘까요?
2. 복잡하고 큰 규모의 서버 코드를 보고싶은데 좋은 예시를 어디서 찾을수 있을까요?
3. 제가 해결방법으로 여러 service를 주입받아서 사용만하는 service를 만들어서 컨트롤러에 있던 코드를 거기로 옮겨서 컨트롤러에 비즈니스 로직이 생기는걸 없에고 순환참조, 코드 중복 등을 제거 해봤는데 이게 맞는건가요?
4. MSA와 상관있는 문제일까요?

답변 5

3

김영한님의 프로필 이미지
김영한
지식공유자

안녕하세요. kevin님

2주 전에 답변을 하셨는데 저희가 질문을 놓셨습니다.

(인프런에서 가끔 메일이 오지 않아서 질문을 놓치는 경우가 있습니다. ㅠㅠ)

1. 적절한 방법이 뭘까요?

코드를 보니 충분히 잘 개선하셨습니다.

2. 복잡하고 큰 규모의 서버 코드를 보고싶은데 좋은 예시를 어디서 찾을수 있을까요? 

복잡하고 큰 규모의 코드는 사실 실무에서 일하지 않는 이상 확인하기 어려운 것 같아요. 이번에 공부하신 예제가 그나마 학습용으로는 어느정도 복잡한 예제입니다.

관련해서 도메인 주도 설계에 대해서 공부해보시면 많은 인사이트를 얻으실 수 있을거에요.

만들면서 배우는 클린 아키텍처라는 책도 괜찮습니다.

3. 제가 해결방법으로 여러 service를 주입받아서 사용만하는 service를 만들어서 컨트롤러에 있던 코드를 거기로 옮겨서 컨트롤러에 비즈니스 로직이 생기는걸 없에고 순환참조, 코드 중복 등을 제거 해봤는데 이게 맞는건가요?

서비스에서 서비스를 주입 받아도 됩니다. 순환 참조가 걱정되면 서비스를 쪼개서 순환 참조가 되지 않도록 개선하면 됩니다.

 4. MSA와 상관있는 문제일까요?

네 MSA와는 관계 없습니다.

감사합니다.

kevin님의 프로필 이미지
kevin
질문자

제가 좀 바보같은 질문을 해서 답이 없으신가...하고 슬퍼하던 중이였는데 다행이네요 ㅎㅎ 감사합니다!

0

kevin님의 프로필 이미지
kevin
질문자

//서비스를 주입받는 서비스
@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class BookComplexService {

    private final BookInfoService bookInfoService;
    private final BookService bookService;
    private final ClubBookUserService cbuService;
    private final UserService userService;
    private final PostService postService;

    @Transactional
    public Long createBookAndRelated(CreateBookDto createBookDto, Long userId) {
        BookInfo bookInfo = BookInfo.builder()
                .name(createBookDto.getName())
                .isbn(createBookDto.getIsbn())
                .build();
        bookInfo = bookInfoService.createOrFindBookInfo(bookInfo);

        Book newBook = Book.builder()
                .category(createBookDto.getCategory())
                .bookInfo(bookInfo)
                .build();
        Long bookId = bookService.createBookWithValidation(newBook, bookInfo.getIsbn(), userId);

        User user = userService.findById(userId);

        ClubBookUser cbu = ClubBookUser.builder()
                .book(newBook)
                .user(user)
                .build();
        cbuService.createClubBookUser(cbu);

        return bookId;
    }

0

kevin님의 프로필 이미지
kevin
질문자

//수정한코드
 @PostMapping
    public ResponseEntity<CreateBookDto> createBook(@SessionAttribute(name = SessionConst.LOGIN_USER, required = false) User loginUser,
                                                    @RequestBody CreateBookDto createBookDto, UriComponentsBuilder b) {

        Long bookId = bookComplexService.createBookAndRelated(createBookDto, loginUser.getId());

        UriComponents uriComponents = b.path("/books/{bookId}").buildAndExpand(bookId);
        return ResponseEntity.created(uriComponents.toUri()).body(createBookDto);
    }

0

kevin님의 프로필 이미지
kevin
질문자

 
//기존 코드
@PostMapping
    public ResponseEntity<CreateBookDto> createBook(@SessionAttribute(name = SessionConst.LOGIN_USER, required = false) User loginUser,
                                                    @RequestBody CreateBookDto createBookDto, UriComponentsBuilder b){
        BookInfo bookInfo = BookInfo.builder()
                .name(createBookDto.getName())
                .isbn(createBookDto.getIsbn())
                .build();

        try {
            bookInfoService.createBookInfo(bookInfo);
        }catch (IllegalStateException e){
            log.info("bookInfoService = {}",e.toString());
            BookInfo byIsbn = bookInfoService.findByIsbn(bookInfo.getIsbn());
            bookInfo = byIsbn;
        }

        Book newBook = Book.builder()
                .category(createBookDto.getCategory())
                .bookInfo(bookInfo)
                .build();

        Long bookId = bookService.createBook(newBook);

        ClubBookUser cbu = ClubBookUser.builder()
                .book(newBook)
                .user(loginUser)
                .build();
        cbuService.createClubBookUser(cbu);

        UriComponents uriComponents =
                b.path("/books/{bookId}").buildAndExpand(bookId);

//        return ResponseEntity.noContent().build().created(uriComponents.toUri()).build();

        return ResponseEntity.created(uriComponents.toUri()).body(createBookDto);
    }

0

안녕하세요. kevin님, 공식 서포터즈 David입니다.

.
말씀하신 내용에 대한 코드를 보여주시면 답변 받으시는데 도움이 되실 것 같습니다:)
.
감사합니다.

kevin님의 프로필 이미지
kevin
질문자

관련 코드 추가했습니다!

kevin님의 프로필 이미지
kevin

작성한 질문수

질문하기