비즈니스 로직(Business Logic)에 대하여

StrayCat·2026년 2월 14일

비즈니스 로직이란

비즈니스 로직(Business Logic)은 컴퓨터 프로그램에서 실제 비즈니스 규칙에 따라 데이터를 생성, 표시, 저장, 변경하는 핵심적인 부분으로, 서비스의 본질적인 동작 방식을 정의하는 코드입니다. 이는 UI(화면)나 DB(저장소)와는 구분되며, "주문 시 재고 차감"이나 "신규 회원 10% 할인"처럼 실제 업무 규칙을 구현하여 기술 환경이 바뀌어도 유지되는 핵심 논리를 의미합니다. - Gemini 3

  • 비즈니스 로직은 한마디로 이 서비스가 돈을 버는 이유가 되는 규칙과 판단
  • 기술적인 코드 (DB저장, HTTP통신, 파일 읽기 등)을 걷어내고 남는 핵심 규칙이 비즈니스 로직

예시 : 쇼핑몰

"회원 등급이 GOLD 이상이면 10% 할인, 쿠폰이 있으면 추가 5% 할인, 단 최대 할인율은 30%를 넘길 수 없다"

이것이 비즈니스 로직이다. DB에 어떻게 저장하는지, API응답을 어떻게 내려주는지는 비즈니슬 로직에 해당하지 않는다.

// 비즈니스 로직 — "무엇을, 왜" 하는가
public int calculateDiscount(Member member, Coupon coupon) {
    int rate = 0;
    if (member.getGrade().isAbove(Grade.GOLD)) rate += 10;
    if (coupon != null && coupon.isValid()) rate += coupon.getRate();
    return Math.min(rate, 30);  // 최대 30%
}

// 기술 로직 — "어떻게" 처리하는가
orderRepository.save(order);           // DB 저장
paymentGateway.requestPayment(amount); // PG사 API 호출
rabbitTemplate.send("order.created");  // 메시지 발행

구분법

구분비즈니스 로직기술(인프라)로직
질문"왜 이렇게 해야 하지?""어떻게 처리하지?"
변경 주체기획자 / PM이 요구개발자가 판단
예시할인 계산, 주문 검증, 등급 산정 등DB 쿼리, API 호출, 캐싱, 트랜잭션 등
바뀌는 시점정책이 바뀔 때기술 스택이 바뀔 때

결국 기획자가 이해할 수 있는 규칙이면 비즈니스 로직, 개발자만 이해하는 처리면 기술 로직 이다.

Q.비즈니스 로직은 말 그대로 '돈 되는 로직'이니까, 보안에 철저해야겠네요.

그렇다. 그리고 그 보안에는 단순히 암호화만 의미하는 것이 아니고, 비즈니스 로직 자체를 보호하는 여러 층위가 존재한다.


비즈니스 로직을 보호하는 방법들

1.입력 검증 - 잘못된 데이터가 로직에 도달하지 못하도록 한다

// Controller 계층 — 형식 검증
@PostMapping("/orders")
public ResponseEntity<?> createOrder(@Valid @RequestBody OrderRequest request) { }

// Entity/Service 계층 — 비즈니스 규칙 검증
public void applyDiscount(int rate) {
    if (rate < 0 || rate > 30) {
        throw new IllegalArgumentException("할인율은 0~30% 사이여야 합니다");
    }
}
  • 클라이언트 검증(Front-End)은 UX용, 서버 검증은 방어선

2.인증 / 인가 - 누가, 무엇을 할 수 있는가

// 인증: 너 누구야? (로그인)
// 인가: 너 이거 할 수 있어? (권한)

@PreAuthorize("hasRole('ADMIN')")
public void cancelOrder(Long orderId) { }

// 또는 서비스에서 직접 검증
public void cancelOrder(Long memberId, Long orderId) {
    Order order = orderRepository.findById(orderId).orElseThrow();
    if (!order.isOwnedBy(memberId)) {
        throw new AccessDeniedException("본인 주문만 취소 가능합니다");
    }
}

3.API 레벨 보호 - 외부 악용 방지

  • 비즈니스 로직이 결국 API로 노출되기 때문에, 요청 자체를 제어해야 한다.
  • Rate Limiting - 할인 쿠폰 발급 API를 1초에 1000번 호출하는 어뷰징 방지
  • 멱등성 보장 - 결제 요청이 중복 실행되어도 한 번만 처리
  • HTTPS - 전송 중 데이터 탈취 방지

4.데이터 무결성 - DB 레벨의 마지막 방어선

애플리케이션 로직이 뚫려도 DB에서 막아주어야 한다.

-- 비즈니스 규칙을 DB 제약조건으로도 보장
ALTER TABLE orders ADD CONSTRAINT chk_amount CHECK (amount > 0);
ALTER TABLE members ADD CONSTRAINT uk_email UNIQUE (email);

계층별 방어 요약

클라이언트 (프론트)     → UX용 검증 (신뢰하지 않음)
    ↓
Controller            → 형식 검증 (@Valid), 인증 확인
    ↓
Service               → 인가 확인, 비즈니스 규칙 검증, 트랜잭션
    ↓
Entity                → 자기 상태 보호 (private 필드, 검증 로직)
    ↓
DB                    → 제약조건 (NOT NULL, UNIQUE, CHECK)
  • 핵심은 한 계층만 믿지 않는 것임
  • 프론트 검증을 우회할 수 있고, 서비스 로직에 버그가 있을 수 있음
  • 따라서 여러 층에서 중복으로 방어함으로 안정성 확보
  • 이를 심층 방어 (Defense in Depth)원칙이라 함

비즈니스 로직은 어디에 작성되는가

Q. MVC 패턴에서 비즈니스 로직은 언제 Entity에 작성하고, 언제 Service에 작성하는가?

A : 핵심 기준 - "이 로직이 자기 자신의 데이터만으로 완결되는가?"

엔티티에 넣는 경우
자기 자신의 필드만으로 판단/변경이 완결되는 로직

@Entity
public class Member {

    private int loginFailCount;
    private MemberStatus status;

    // ✅ 자기 필드만 사용 → 엔티티에 작성
    public void loginFailed() {
        this.loginFailCount++;
        if (this.loginFailCount >= 5) {
            this.status = MemberStatus.BLOCKED;
        }
    }

    public void changeNickname(String nickname) {
        if (nickname.length() > 20) {
            throw new IllegalArgumentException("닉네임은 20자 이하");
        }
        this.nickname = nickname;
    }
}

서비스에 넣는 경우
다른 엔티티, 외부 시스템, repository 조회 등이 필요한 로직

@Service
@RequiredArgsConstructor
@Transactional
public class OrderService {

    private final OrderRepository orderRepository;
    private final MemberRepository memberRepository;
    private final PaymentGateway paymentGateway;  // 외부 시스템

    // ✅ 여러 엔티티 + 외부 시스템 조합 → 서비스에 작성
    public Long createOrder(Long memberId, Long productId, int quantity) {
        Member member = memberRepository.findById(memberId)
                .orElseThrow(() -> new IllegalArgumentException("회원 없음"));

        Product product = productRepository.findById(productId)
                .orElseThrow(() -> new IllegalArgumentException("상품 없음"));

        Order order = Order.create(member, product, quantity);  // 엔티티의 생성 로직
        paymentGateway.requestPayment(order.getTotalPrice());   // 외부 API 호출
        orderRepository.save(order);

        return order.getId();
    }
}

Entity
- 자기 필드만으로 완결
- 유효성 검증 (자기 데이터)
- 상태 변경 (자기 상태)

Service
- 다른 엔티티와 협력 필요
- Repository 조회 필요
- 외부 API / 시스템 호출
- 트랜잭션 조율

  • 이러한 설계 방식을 도메인 모델 패턴(Rich Domain Model)이라 부름
  • 반대로 엔티티를 getter/setter만 가진 데이터 집합체로 간주하고 로직을 전부 서비스에 몰아넣는 것을 트랜잭션 스크립트 패넡(Anemic Domain Model)이라 칭함
  • 실무에서는 둘을 혼용하되, 엔티티가 스스로 할 수 있는 것은 엔티티에게 위임 하는 원칙을 지키는 것이 책임 분리에 용이함.
  • 서비스가 비대해 지는 것을 막는 좋은 방법임

profile
알면 좋은 것보단 잊어버리기 싫은 것들을 기록합니다.

0개의 댓글