@Component
@Profile("!ops")
public class AbcSimulator {
private TcpServer server;
public AbcSimulator() {
start(12345);
}
public void start(int bindPort) {
server = new TcpServer();
AbcPipelineInitializer pipelineInitializer = new AbcPipelineInitializer(server);
server.init(bindPort, pipelineInitializer);
server.start();
}
...
}
원인은 스프링 컨텍스트가 시뮬레이터 빈을 생성하다가 서버 포트가 이미 바인딩되어 있다는 예외를 만나게 되는데 예외를 어디에서도 처리하지 않기 때문이었다. 아마도 테스트용이라 무지성으로 간단히 처음 버전을 만들었는데 뒤이어 만드는 시뮬레이터들도 별 생각 없이 다 똑같이 만든 것 같다. (부끄러움)
서버 포트가 왜 이미 바인딩되어 있는지가 더 핵심적인 원인이지만 포트 바인딩은 서비스 외부적인 요인에 의해서 일어날 수 있는 일이고 하나의 시뮬레이터가 생성 실패했다고 모든 서비스가 실행되지 않는 일은 피해야 겠다. 일단 오류 메시지를 출력해 주고 해당 하드웨어 시뮬레이터만 로딩 실패하도록 만들어보자.
예외 처리를 해주려고 보니 N개의 시뮬레이터 서버 실행 코드에 아래와 같은 try-catcy 문장이 반복해서 들어가야 했다.
try {
server = new TcpServer();
AbcPipelineInitializer pipelineInitializer = new AbcPipelineInitializer(server);
server.init(bindPort, pipelineInitializer);
server.start();
log.info(simulatorName + " : start success.");
} catch (Exception e) {
log.error(simulatorName + " : start fails.", e);
}
간단한 반복이지만 변경되지 않는 템플릿 코드와 콜백 형태로 템플릿에 전달할 수 있는 변경되는 코드 부분이 극명하게 보였다. try-catch 문장과 로그를 남기는 코드는 변하지 않는 부분이라 할 수 있고, 서버를 생성하고 초기화 하는 부분과 로그로 남기는 시뮬레이터 이름은 변경되는 부분이라고 할 수 있겠다. 배웠던 템플릿/콜백 패턴을 적용해 보기 적합한 예제인 것 같다. 최근에 동료가 파일 IO를 처리하며 예외처리 지옥이라고 괴로워하던 생각도 났다.
@Slf4j
@Component
public class ServerStartTemplate {
public void work(Runnable serverStartCallback, String simulatorName) {
try {
serverStartCallback.run();
log.info(simulatorName + " : start success.");
} catch (Exception e) {
log.error(simulatorName + " : start fails.", e);
}
}
}
@Component
@Profile("!ops")
@RequiredArgsConstructor
public class AbcSimulator {
private final ServerStartTemplate serverStartTemplate;
private TcpServer server;
@PostConstruct
public void start() {
serverStartTemplate.work(() -> {
start(12345);
}, "Abc Simulator");
}
private void start(int bindPort) {
server = new TcpServer();
AbcPipelineInitializer pipelineInitializer = new AbcPipelineInitializer(server);
server.init(bindPort, pipelineInitializer);
server.start();
}
...
}
N번 반복해야 했던 변하지 않는 예외처리와 로깅 코드를 템플릿으로 분리하고 변경되는 서버 생성과 초기화 부분은 콜백으로 전달해서 깔끔하게 처리된 것 같다. 배운 것을 이렇게 적용해 보니 너무 기분이 좋다.