스터디 주제
오늘의 핵심 주제는 Spring Webflux와 Reactive Programming, Git의 운영 방식, AOP(Aspect-Oriented Programming), 그리고 특정 프로젝트 내용의 최적화 방안에 대한 심층 분석이다.
Spring WebFlux와 Reactive Streaming
리액티브 프로그래밍은 비동기 데이터 처리와 이벤트 기반 시스템의 효율성을 극대화하는 프로그래밍 패러다임이다. 특히, 리액티브 프로그래밍의 ‘Non-blocking IO’ 특성은 시스템의 처리량과 응답성을 대폭 향상시킨다. PUB/SUB 모델을 기반으로 하는 이 패러다임은, 데이터가 준비될 때까지 기다리는 대신, 데이터가 방출되는 즉시 이를 구독하고 처리함으로써, 애플리케이션의 확장성과 반응성을 높일 수 있다. Spring WebFlux에서는 Mono
와 Flux
와 같은 리액티브 스트리밍 타입을 통해 실현되며, 이들을 사용하여 다양한 데이터 처리 시나리오를 지원한다.
기준 | Reactive Programming(반응형 프로그래밍) | Imperative Programming (명령형 프로그래밍) |
---|---|---|
처리 방식 | 비동기적, 데이터 스트림과 이벤트에 의존 | 동기적, 명령어의 순차적 실행에 의존 |
프로그램의 흐름 | 데이터 흐름과 변화의 전파에 중점 | 명령어의 명시적인 흐름 제어 |
대응 모델 | PUB/SUB 모델, 구독자가 데이터의 변화를 감지하고 반응 | 직접적인 함수 호출로 결과를 반환 |
사용 사례 | 고성능 비동기 처리가 필요한 어플리케이션, 실시간 데이터 처리 | 일반적인 어플리케이션, 순차적 로직이 중요한 경우 |
주요 이점 | 확장성, 비동기 처리, 반응성 | 단순성, 직관적인 코드 흐름, 쉬운 디버깅 |
Git Flow / Github Flow / GitLab Flow
- Git Flow: 기능 개발 시
feature
브랜치에서 작업 후develop
으로 병합. 릴리스 준비 시release
브랜치를 생성하고, 완료 후master
와develop
에 병합. 긴급 수정 필요 시hotfix
브랜치 사용. - GitHub Flow: 모든 개발 작업을
feature
브랜치에서 진행 후, 코드 리뷰를 거쳐master
브랜치에 직접 병합. 단순하고 지속적인 배포를 지원. - GitLab Flow:
feature
브랜치에서 개발,master
브랜치로 병합. 환경 별로production
,staging
등의 브랜치를 사용하여 배포 관리. GitLab CI/CD와 통합하여 더 효율적인 배포 프로세스 구현.
Flow별 장단점
- Git Flow: 복잡한 릴리스와 핫픽스 관리에 강점. 구조가 복잡하고 학습 곡선이 있음.
- GitHub Flow: 단순성과 빠른 배포에 유리. 복잡한 릴리스 관리에는 한계.
- GitLab Flow: 유연성과 보안성이 강점. 설정과 관리에 더 많은 노력이 필요.
Flow별 배포 및 버저닝
- Git Flow:
release
브랜치에서 버전을 관리하고,master
로 병합 시 태그를 사용하여 버전을 명시. - GitHub Flow:
master
브랜치에서 직접 배포. 배포 시점에서 태그를 사용해 버저닝. - GitLab Flow: 환경 별 배포 브랜치를 통한 배포 관리. 필요에 따라 태그를 사용하여 버저닝.
rebase? squash?
Git에서는 효율적인 버전 관리와 협업을 위해 rebase
와 squash
같은 기능을 활용한다.rebase
는 중복 커밋 없이 타겟 브랜치에 변경 사항을 통합하는 방법으로, 깔끔한 커밋 히스토리를 유지하게 돕는다. 반면, squash
는 여러 커밋을 하나로 합쳐 메세지를 단순화한다.
LUCYCATO 프로젝트 코드 분석
LoggingAspect
@Aspect
@Component
@RequiredArgsConstructor
public class LoggingAspect {
private final LoggingProducer loggingProducer;
@Before("execution(* org.lucycato.*.adapter.in.web.*.*(..))")
public void beforeMethodExecution(JoinPoint joinPoint) {
// * Mono: 0 ~ 1개를 방출, Flux: 0 ~ N개 방출
// * 즉 Mono는 1개 방출 후 dispose 된다 (방출 후 dispose 된다는 것은 메모리 누수가 발생을 안한다.)
Mono.just(joinPoint.getSignature().getName())
.flatMap(methodName -> loggingProducer.sendLogMessage("logging", "Before executing method: %s".formatted(methodName)))
.subscribe();
}
}
Spring Boot에서 @Aspect
는 AOP의 핵심으로, 반복되는 보조 로직을 최소화할 수 있는 도구이다. @Aspect
를 적용한LoggingAspect
는 AOP를 통해 로깅 로직을 애플리케이션 전반에 걸쳐 쉽게 적용할 수 있게 해준다.
ObjectMapperConfig
프로젝트 내부에서, ObjectMapper = new ObjectMapper()
로, 객체를 사용하는 것이 아니라, ObjectMapperConfig
의 생성과 커스텀 ObjectMapper을 Bean에 등록함으로써 JSON 객체 변환 시 발생할 수 있는 타입 오류를 방지한다. 이를 통해 데이터의 안정성과 신뢰성을 보장한다
public class ObjectMapperConfig {
@Bean
public ObjectMapper objectMapper() {
com.fasterxml.jackson.databind.ObjectMapper objectMapper = new com.fasterxml.jackson.databind.ObjectMapper();
objectMapper.registerModule(new Jdk8Module());
objectMapper.registerModule(new JavaTimeModule());
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
objectMapper.setPropertyNamingStrategy(new PropertyNamingStrategies.LowerCamelCaseStrategy());
return objectMapper;
}
}
- 커스텀 어노테이션,
@ProducerAdapter
package org.lucycato.common.annotation.out;
import org.springframework.core.annotation.AliasFor;
import org.springframework.stereotype.Component;
import java.lang.annotation.*;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface ProducerAdapter {
@AliasFor(annotation = Component.class)
String value() default "";
}
@ProducerAdapter
같은 커스텀 어노테이션을 통해 헥사고날 아키텍처 내에서 데이터가 어떤 경로로 이동하는지 명확히 할 수 있다.