핵심 비지니스 로직 외의 후처리 작업은 어떻게 구현하는 것이 좋을까?

2013-02-17 18:13

비지니스 로직을 구현하다 보면 핵심 비지니스 로직 외에 처리해야 되는 작업들이 많이 발생한다. 특히 글을 쓰거나, 답변을 다는 경우와 같이 데이터의 상태를 변경하는 경우 이 같은 작업이 필요한 경우가 많다.

현재 slipp.net의 소스 코드를 통해 어떤 상황인지 살펴보면 다음과 같다.

  • slipp.net은 답변을 하는 경우 질문과 답변자 중 facebook 사용자의 경우 알림을 전송한다.
  • 답변을 할 때 facebook으로 전송하도록 설정하면 답변을 facebook으로 전송할 수 있다.

위 두 가지 요구사항은 핵심 비지니스 로직이 아니다. 일반적인 위와 같은 요구사항은 다음과 같이 처리한다.

    public void createAnswer(SocialUser loginUser, Long questionId, Answer answer) {
        Question question = questionRepository.findOne(questionId);
        answer.writedBy(loginUser);
        answer.answerTo(question);
        Answer savedAnswer = answerRepository.saveAndFlush(answer);
        notificationService.notifyToFacebook(loginUser, question, question.findNotificationUser(loginUser));
        if (answer.isConnected()) {
        	facebookService.sendToAnswerMessage(loginUser, savedAnswer.getAnswerId());
        }
    }

위와 같이 알림을 담당하는 Service와 메시지 전송을 담당하는 Service에 위임한다. NotificationService 구현부를 살펴보면 다음과 같다.

package net.slipp.service.qna;


[...]


@Service
public class NotificationService {
	@Value("#{applicationProperties['facebook.accessToken']}")
	private String accessToken;
	
	@SuppressWarnings({ "unchecked", "rawtypes" })
	@Async
	public void notifyToFacebook(SocialUser loginUser, Question question, Set<SocialUser> notifierUsers) {
		if (notifierUsers.isEmpty()) {
			return;
		}
		
		for (SocialUser socialUser : notifierUsers) {
			[...]		
		}
	}
}

위와 같이 후처리 작업이 많지 않은 경우에는 각 작업을 위와 같이 분리한 후 호출하는 방식으로 구현해도 충분하다. 하지만 서비스를 운영하다보면 요구사항이 계속 증가하면서 위와 같이 후처리해야 되는 작업이 증가하는 경우가 많다. 이로 인해 핵심 비지니스 로직을 구현하는 부분보다 후처리 작업이 더 많아져 유지보수 하기 힘든 상황이 발생한다. 이와 같은 상황이 발생할 경우 어떻게 개선하는 것이 좋을가?

Aspect를 추가해 해결할 수도 있겠지만 배보다 배꼽이 커지는 경우를 많이 봐왔다. 좀 더 깔끔하게 개선할 수 있는 방법이 없을까?

1개의 의견 from FB

BEST 의견 원본위치로↓
2013-02-17 22:03

이럴 때 쓰라고 큐가 있죠.

RabbitMQ, ActiveMQ 등의 복잡하고 거대한 큐가 아닌, Rails 기준으로는 Delayed Job 이나, Resque 등의 단순한 큐로 어느정도 수준 이상은 다 감당 가능해 보입니다.

http://kr.github.com/beanstalkd/ 이런 단순한 녀석을 사용하셔도 좋고, 0mq http://www.zeromq.org/ 같은 녀석을 이용하셔도 좋습니다.

어차피, 후처리 작업의 경우 대부분 비동기로 이뤄져도 상관 없는 녀석이기 때문에, 비동기 처리가 가장 적합한 방법이라고 봅니다.

8개의 의견 from SLiPP

2013-02-17 22:03

이럴 때 쓰라고 큐가 있죠.

RabbitMQ, ActiveMQ 등의 복잡하고 거대한 큐가 아닌, Rails 기준으로는 Delayed Job 이나, Resque 등의 단순한 큐로 어느정도 수준 이상은 다 감당 가능해 보입니다.

http://kr.github.com/beanstalkd/ 이런 단순한 녀석을 사용하셔도 좋고, 0mq http://www.zeromq.org/ 같은 녀석을 이용하셔도 좋습니다.

어차피, 후처리 작업의 경우 대부분 비동기로 이뤄져도 상관 없는 녀석이기 때문에, 비동기 처리가 가장 적합한 방법이라고 봅니다.

2013-02-18 09:42

@ezblog @Kenny 지금까지 나온 의견을 바탕으로 단계적으로 접근한다면 다음과 같은 접근 방법이 가능할 듯하다.

  • 처음 구현은 요구사항이 단순한 경우가 대부분이기 때문에 원글에서 내가 구현한 접근방식으로 구현한다. 사실 국내 대부분의 경우에는 이 1단계에서 중단하는 경우가 대부분이다.
  • 2단계는 후처리 작업이 많아지는 경우 희송이 의견처럼 후처리를 위한 interface를 하나 만들고 후처리 각 작업을 담당하는 구현체를 만든 후 Collection에 등록해 for문 돌리면서 후처리 작업한다.
  • 2단계에서 성능 상의 이슈가 있거나 명확한 분리를 위해 Kenny가 제안한 대로 MQ로 접근하는 방법이면 되지 않을까라는 생각이 든다. 이와 같이 진행한다면 2단계에서 구현한 인터페이스와 구현 클래스가 모두 MQ 서버로 연동했을 때도 재사용 가능하리라 생각된다.

@Kenny MQ 관련해서 Rails의 경우에는 Delayed Job 이나, Resque와 같은 놈 활용하면 쉽게 접근할 수 있다고 했는데 자바 기반으로 접근했을 때는 네가 뒤에 추천한 놈으로 접근하면 괜찮으려나? 자바 진영에서는 MQ 사용에 상당히 비관적인 경우도 많고, 아예 생각조차 못하는 경우도 많아서..

2013-02-18 09:49

@자바지기

뭐... 데이터가 완벽하게 동기화 된다는 환상이 있으면 큐 사용에 비관적일 수 있겠죠. 대부분의 경우에 문제 없습니다. 뒤에 적어 놓은 녀석들의 경우에는 생각보다 활발하게 사용되고 있어요. 특별히 성능상의 이슈도 없고. (이슈가 생길 정도로 복잡한 녀석들이 아닙니다... 오히려, 저 녀석들을 큐 라고 불러야 하나 라는 이야기가 나올 정도로 단순한 녀석들이에요.)

어차피 JVM In-Memory Queue를 사용하시건, 외부 Queue를 사용하시건, 목적이 다른 녀석들이기 때문에 Queue를 안쓰실 수는 없을꺼에요. 필요하면 써야죠. ㅎㅎ

2013-02-18 10:04

@Kenny MQ 기반으로 아키텍처를 가져갈 때의 일반적인 구현 방식이 궁금하다.

클라이언트 event => 웹 서버 => MQ => 후처리 구현

위와 같은 방식으로 동작하게 될텐데 여기서 후처리 구현은 보통 어떤 식으로 구현하는 것이 좋을까? 예를 들어 위와 같이 자바 기반으로 구현되어 있는 소스 코드를 재사용하려면 MQ에 java로 구현된 listener를 등록하면 가능할 듯 한데, 그렇지 않은 경우들도 있잖아. 만약 이럴 경우에는 그럼 MQ에서 사용하는 listener를 등록해 놓고 이 listener가 후처리 구현을 하는 놈으로 위임하는 구조로 진행해야 하는 걸까?

아직 MQ에 대해 실무에서 직접적으로 경험한 적이 없다보니 막연한 부분이 많네. 아님 MQ 관련한 아키텍처를 학습할 수 있는 문서를 추천해 주어도 도움이 많이 되겠다.

2013-02-18 14:21

이런 식의 메세지 기반 서비스를 지원하기 위해 spring-integration이 있습니다. rabbitmq와 연동해서 spring-integration-amqp를 쓰고 있구요 이 외에도 꽤 많은 프로토콜/컴포넌트를 지원합니다.

설정만으로 이용이 가능하지만 늘 그렇듯이 제대로 쓰려면 해당 프로토콜/컴포넌트의 이해와 구현된 스프링 소스까지 봐야 안심이 되더라구요.

2013-02-18 23:03

비동기 처리면 당근 큐가 좋은데... 동기처리가 꽤 섞여 있더라구요... 저의 경험상..

그래서 저와 케니가 말한것 둘 같이 쓰면 좋을것 같습니다. 비동기로 처리가 되는 것은 후처리 클래스에서 큐에 넘기면 되니까요...

2013-03-02 21:19

@자바지기 EIP를 보시면 됩니다. 문서도 있고 책도 있습니다. apache camel이 구현체인데요. DSL기반의 설정으로 쓸만합니다.

이벤트 기반의 후처리는 로직에 대한 검토를 잘해서 동기/비동기를 선택해야합니다. 이벤트의 라이프싸이클도 중요하고요. 일단, 메인 비즈니스로직과 후처리를 도메인과 트랜잭션을 감안해서 먼저 설계가 필요합니다.다음에 블로그에서 적용했던 다이어그램 보여드릴게요.

의견 추가하기

연관태그

← 목록으로