Post

스프링 | Spring


KT 에이블스쿨 6기 Spring에 진행한 강의 내용 정리한 글입니다.

Web

HTTP 프로토콜을 기반으로 TCP/IP 네트워크상에서 HTML을 통해 작성된 하이퍼텍스트를 포함한 콘텐츠를 제공해 서비스한 것이 웹의 시초

  • HTML(HyperTextMarkup Language): 웹페이지가 어떻게 구조화되어 있는지 브라우저로 하여금 알 수 있도록 하는 마크업 언어
  • 하이퍼텍스트(HyperText): 웹페이지에서 다른 페이지로 이동할 수 있도록 하는 것
  • 프로토콜(Protocol): 네트워크에 연결된 컴퓨터들간의 통신 규약

HTTP

텍스트 기반의 통신규약으로 웹 클라이언트와 웹 서버간에 데이터를 주고 받는데 사용되는 프로토콜

주요 HTTP Method

HTTP Method설명
POST서버로 요청 데이터를 전달
GET특정 리소스를 조회
PUT특정 리소스를 대체, 만일 리소스가 없다면 생성
PATCH리소스의 일부를 변경
DELETE특정 리소스를 삭제

HTTP Status code

HTTP 상태코드는특정HTTP 요청이성공적으로완료되었는지알려준다

  • 2xx: Success
    • 서버가 클라이언트로부터 전달받은 요청을 성공적으로 수행
상태 코드의미
200 OK요청이 성공적으로 이루어졌음
201 Created요청이 성공적이었으며, 그 결과로 새로운 리소스가 생성됨
202 Accepted요청을 수신하였으나, 아직 처리하지 않음
204 No Content요청을 성공적으로 처리했지만, 콘텐츠를 제공하지 않음
  • 3xx: Redirection
    • 클라이언트가 보낸 요청을 완료하려면 서버에서 아직 추가적인 작업(페이지이동)이 필요
  • 4xx: Client Error
    • 클라이언트가 서버에 잘못된 요청을 했을 경우 발생
상태 코드의미
400 Bad Request잘못된 문법으로 인해 서버가 이해할 수 없음
401 Unauthorized해당 요청은 인증이 필요함
403 Forbidden클라이언트는 콘텐츠에 접근할 권리를 가지고 있지 않음
404 Not Found서버는 요청받은 리소스를 찾을 수 없음
405 Method Not Allowed클라이언트의 요청이 허용되지 않는 메서드일 때 발생함
409 Conflict클라이언트의 요청이 서버의 상태와 충돌이 발생함
  • 5xx: Server Error
    • 서버가 클라이언트의 요청을 처리하지 못했을 때 발생
상태 코드의미
500 Internal Server Error서버에 오류가 발생하여 요청을 수행할 수 없음
503 Service Unavailable서버가 요청을 처리할 준비가 되지 않음

TCP

두 개의 호스트를 연결하고 데이터 스트림을 교환하게 해주는 네트워크 프로토콜
TCP는 데이터와 패킷이 보내진 순서대로 전달하는 것을 보장

  • 애플리케이션 계층 (Application Layer)
    • 응용 계층은 사용자가 네트워크에 접근할 수 있도록 함
    • 사용자 인터페이스를 제공할 뿐 아니라 이메일, 원격 파일 접근 및 전송, 공유 데이터베이스 관리 등의 서비스를 제공
  • 전송 계층 (Transport Layer)
    • 전송 계층은 전송을 담당하는 계층
    • TCP 뿐만 아니라 사용자 데이터그램 통신 규약(UDP)도 존재
  • 인터넷 계층 (Internet Layer)
    • 인터넷 계층은 네트워크 간 데이터 패킷의 전송을 관리
    • 데이터를 목적지까지 효율적으로 전달하는 방법을 담당
  • 네트워크 액세스 계층 (Network Access(Interface) Layer)
    • 데이터 전송의 최하위 계층으로 네트워크 인터페이스 계층
    • 데이터를 전기 신호로 변환한 뒤, 물리적 주소인 MAC 주소를 사용하여 알맞은 기기로 데이터를 전달하는 계층

IP(Internet Protocol)

데이터의 조각들을 최대한 빠르게 목적지까지 보내는 역할
TCP/IP로 연결된 네트워크에서 각각의 컴퓨터를 구분하기 위해 사용하는 주소

TCP/IP

TCP/IP는 H/W, OS, 접속 매체와 관계 없이 동작할 수 있는 개방형 구조로 이루어져 인터넷의 기반 프로토콜로 자리 잡음

  • TCP: 데이터 흐름 관리, 데이터 정확성 확인 등의 역할을 수행
  • IP: 데이터를 목적지까지 전 송하는 역할을 담당

포트

  • IP 내에서 애플리케이션의 프로세스 구분을 위해 사용하는 번호
  • 포트 숫자는 IP 주소가 가리키는 PC에 접속할 수 있는 채널을 의미 (ex. 80 → HTTP / 443 → HTTPS)

DNS

  • 사용자가 도메인 이름을 사용하여 웹 사이트에 접속했을 때, 해당 도메인을 실제 네트워크상의 IP 주소로 변환하여 접속하도록 함
  • 클라이언트로부터 URL 주소를 받아 해당 URL에 해당하는 IP 주소를 반환하는 역할 수행

웹의 동작 구조

  1. 웹 브라우저에서 도메인을 입력
  2. DNS 서버는 해당 도메인을 가진 IP 주소를 웹 브라우저에게 전송
  3. 웹 브라우저는 IP 주소의 해당 서버에 접속을 시도
    웹 서버는 접속을 기다리다 접속 요청이 들어오면 수락
  4. 웹 서버는 요청 내용을 분석하고 요청된 [index.html] 파일을 읽음
  5. 웹 서버는 파일 내용을 클라이언트에 전송
  6. 웹 브라우저는 웹 서버로부터 받은 내용을 이용하여 HTML 태그를 분석해 화면을 구성

CSR

클라이언트인 브라우저가 렌더링을 처리하는 방식으로 서버에서 받은 데이터를 통해 클라이언 트인 브라우저가 화면(View)를 그리는 주체가 됨

CSR 동작 과정

  1. 클라이언트가 웹 사이트 요청
  2. CDN이 HTML 파일과 자바스크립트로 접근할 수 있는 링크를 클라이언트에게 전송
  3. 클라이언트는 HTML과 자바스크립트를 다운로드
  4. 다운로드가 완료된 HTML과 자바스크립트가 웹 브라우저에서 실행
  5. API로부터 받아온 데이터를 넣어주면 페이지 상호작용이 가능

SSR

서버 쪽에서 렌더링 준비를 끝마친 상태로 클라이언트에 전달하는 방식

SSR 동작 과정

  1. 클라이언트가 웹 사이트 요청
  2. 서버는 즉시 렌더링이 가능한 HTML 파일을 만들어 클라이언트에 전달
  3. 클라이언트에 전달되는 순간 HTML은 즉시 렌더링 되어 화면을 표시
  4. 클라이언트가 자바스크립트를 다운로드
  5. 브라우저가 자바스크립트 프레임워크를 실행
  6. 자바스크립트가 성공적으로 컴파일 되면 웹 페이지의 상호작용이 가능

웹 프로젝트의 기본 구조

  • 3-Tier Architecture (3계층 구조)
    • 프리젠테이션 계층 (클라이언트)
      • 서버에서 응답(Response) 받은 결과를 화면에 렌더링하여 사용자에게 보여주고 서버에 원하는 데이터를 요청(Request) 하는 프로그램
      • 해당 계층은 front-end 라고도 불림
    • 애플리케이션 계층 (서버)
      • 클라이언트에게 서비스를 제공하는 시스템으로, 클라이언트의 요청(Request)에 데이터를 가공하여 응답(Response)하는 시스템
      • 해당 계층은 미들웨어 또는 back-end로 불림
    • 데이터 계층 (데이터베이스)
      • 애플리케이션에서 사용되는 데이터를 영구적으로 저장하고 관리하는 부분
      • 데이터베이스에 접근하여 데이터를 읽거나 쓰는 것을 관리하는 계층

데이터베이스

데이터 계층 또는 데이터베이스는 애플리케이션에서 사용되는 데이터를 영구적으로 저장하고 관리하는 부분

데이터베이스 관리 시스템 (DBMS)

  • 데이터를 효율적으로 저장, 검색, 수정, 삭제, 추가할 수 있도록 관리하는 시스템
    • 주요 DBMS:MySQL, Oracle, PostgreSQL, MongoDB 등
  • 각 데이터베이스는 특정 용도, 성능, 확장성 등을 고려하여 선택

데이터베이스의 구조

데이터를 테이블(Table) 형태로 저장하며, 테이블은 여러 개의 열(Column)로 구성
필요에 따라 검색, 수정, 삭제, 추가(CRUD 작업) 수행 가능

데이터 계층의 역할

  • 애플리케이션과 데이터베이스 간의 상호작용 관리
  • 데이터 검색, 수정, 삭제, 추가 등 CRUD 작업 수행
  • 애플리케이션의 요청에 따라 필요한 정보를 제공

Spring Framework

Framework

프레임워크는 애플리케이션 개발 시 필수적인 코드, 알고리즘 등의 기능을 위해 뼈대를 제공
개발자는 이런 뼈대 위에서 코드를 작성하여 애플리케이션을 개발 가능

Library

라이브러리란 주로 소프트웨어를 개발할 때 필요한 기능(함수)들을 모아 놓은 소프트웨어
특정 작업을 수행하는 기능이나 메소드의 모음

Framework와 Library의 차이점

구분FrameworkLibrary
제어 흐름프레임워크가 전체적인 흐름을 제어개발자가 필요할 때 호출하여 사용
제어의 역전 (IoC)적용됨
객체의 생성과 소멸을 프레임워크가 관리
적용되지 않음
개발자가 직접 객체를 관리
개발자의 역할제공된 구조 내에서 코드를 작성필요한 기능을 직접 호출하여 사용

의존성 주입

클래스의 인스턴스를 생성하여 의존성 주입

의존성(Dependency): 한 객체가 다른 객체를 직접 생성하거나 호출할 때, 두 객체 간의 의존성이 발생

1
2
3
4
5
6
7
8
public class MemberService {
    private MemberRepository memberRepository = new MemberRepository();

    public void createMember(String email) {
        Member member = new Member(email);
        memberRepository.save(member);
    }
}

생성자를 통한 의존성 주입

Spring 프레임워크의 IoC(Inversion of Control, 제어의 역전) 원칙을 활용하여 객체의 생성과 관리 책임을 프레임워크가 담당

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Service
public class MemberService {
    private final MemberRepository memberRepository;

    @Autowired
    public MemberService(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }

    public void createMember(String email) {  
        Member member = new Member(email);  
        memberRepository.save(member);  
    }
}

Spring Framework

Java 플랫폼을 위한 오픈 소스 애플리케이션 프레임워크로써 엔터프라이즈급 애플리케이션을 개발하기 위한 모든 기능을 종합적으로 제공하는 경량화된 솔루션

  • 스프링은 Java 언어 기반의 프레임워크
  • Java 언어의 가장 큰 특징은 객체 지향 언어
    • 스프링은 객체 지향 언어가 가진 가장 강력한 특징을 살려내면서 애플리케이션 제작이 가능

Spring Framework의 특징

POJO(Plain Old Java Object) 기반의 구성

객체 지향적인 원리에 충실하면서도 특정 환경이나 기술에 종속되지 않고 필요에 따라 재활용할 수 있는 방식으로 설계된 오브젝트

제어의 역행(IoC)과 의존성 주입(DI)을 통한 객체 간의 구성

  • 제어의 역행(Inversion Of Control)
    • 코드의 최종 호출을 개발자가 아닌 스프링 프레임워크가 담당
    • 코드 제어의 흐름이 프레임워크에 종속
    • 사용자가 new 연산자를 사용하여 객체를 생성하는 것이 아니라, 스프링 컨테이너가 객체의 생명 주기를 관리
  • 의존성 주입 (Dependency Injection, DI)
    • MemberController 클래스가 MemberServicegetMemberList() 메서드를 사용할 때, MemberControllerMemberService에 의존
    • 스프링에서는 의존성 주입을 자동으로 수행하여 클래스 간의 결합도를 낮추고 유지보수성을 향상

경량 컨테이너

스프링 프레임워크는 개발자를 대신하여 Bean(객체)을 생성 및 관리하는 컨테이너를 갖고 있으며 이를 스프링 컨테테이너라고 함

  • 스프링 컨테이너(Spring Container): 스프링에서 자바 객체들을 관리하는 공간
    • 스프링 컨테이너는 빈(Bean)의 생성부터 소멸까지 개발자 대신 관리
    • 스프링 컨테이너에 객체, 빈(Bean)을 등록하는 이유는 객체간의 의존 관계들을 스프링이 관리하도록 하기 위해서다
  • 빈(Bean): 스프링 컨테이너가 관리하는 자바 객체

AOP(관점 지향 프로그래밍) 지원

스프링 프레임워크에서 제공하는 기능 중 하나로 관점 지향 프로그래밍을 지원하는 기술

  • 로깅, 보안, 트랜잭션과 같은 공통적인 관심사를 모듈화
  • 코드 중복을 줄이고 유지보수성을 향상시키는 데 도움을 줌

OOP & POJO

image

POJO: 객체 지향적 원리에 충실하면서 기술에 종속되지 않고 재활용 가능하도록 설계된 방식

객체 지향 프로그래밍 & 절차 지향 프로그래밍

절차 지향 프로그래밍

물이 위에서 아래로 흐르는 것처럼 순차적인 처리가 중요시되며, 프로그램 전체가 유기적으로 연결되도록 만드는 프로그래밍 기법

객체 지향 프로그래밍

프로그램을 객체 단위로 나누고 객체의 유기적인 상호작용을 통해 로직을 구성하는 방식

  1. 캡슐화 (Encapsulation)
    • 낮은 결합도를 유지할 수 있도록 설계하는 것
    • 객체의 내부 구현을 감춤(은닉)으로써 한 곳에서 변화가 일어나더라도 다른 곳에 미치는 영향을 최소화
    • 접근 제어자: private, default, protected, public
    접근 제어자같은 클래스의 멤버같은 패키지의 멤버자식 클래스의 멤버
    publicOOO
    protectedOOO
    defaultOOX
    privateOXX
  2. 상속 (Inheritance)
    • 자식 클래스가 부모 클래스의 기능을 상속받아 재사용하는 개념
    • 상속을 사용하면 코드 중복을 최소화 가능
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    
     class Animal {
         void makeSound() {
             System.out.println("Some generic sound");
         }
     }
    
     class Cat extends Animal {
         void scratch() {
             System.out.println("Cat scratches");
         }
    
         void jump() {
             System.out.println("Cat jumps");
         }
     }
    
     class Dog extends Animal {
         void dig() {
             System.out.println("Dog digs");
         }
    
         void run() {
             System.out.println("Dog runs");
         }
     }
    
  3. 다형성 (Polymorphism)
    • 한 객체가 다른 여러 형태(객체)로 재구성될 수 있는 특성
    • Overload(오버로딩), Override(오버라이딩)을 통해 다형성을 구현 가능
      • 오버로딩 (Overloading)
        • 같은 클래스 내에서 같은 이름의 메서드를 여러 개 정의하는 것
        • 매개변수의 개수 또는 타입이 다르면 같은 이름의 메서드를 사용 가능
      • 오버라이딩 (Overriding)
        • 상속받은 메서드를 재정의하여 기능을 확장하는 것
        • 부모 클래스의 메서드를 자식 클래스에서 다시 구현함으로써 코드 중복을 줄일 수 있음
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    
     class Animal {
         void makeSound() {
             System.out.println("Some generic sound");
         }
     }
    
     class Cat extends Animal {
         @Override
         void makeSound() {
             System.out.println("Meow");
         }
     }
    
     class Dog extends Animal {
         @Override
         void makeSound() {
             System.out.println("Bark");
         }
     }
    
  4. 추상화
    • 공통의 속성이나 기능을 묶어 이름을 붙이는 것
    • 불필요한 세부 사항은 제거하고 핵심적인 부분만 추출하여 정의
    • 객체의 공통적인 속성과 기능을 추출하여 클래스나 인터페이스로 정의
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    
     abstract class Vehicle {
         protected int capacity;
         protected int speed;
    
         Vehicle(int capacity) {
             this.capacity = capacity;
             this.speed = 0;
         }
    
         abstract void move();
         abstract void stop();
    
         void displayInfo() {
             System.out.println("Capacity: " + capacity + ", Speed: " + speed);
         }
     }
    
     class Sedan extends Vehicle {
         Sedan() {
             super(5);
         }
    
         @Override
         void move() {
             speed = 60;
             System.out.println("Sedan moves at " + speed + " km/h");
         }
    
         @Override
         void stop() {
             speed = 0;
             System.out.println("Sedan stops");
         }
     }
    
     class Truck extends Vehicle {
         Truck() {
             super(2);
         }
    
         @Override
         void move() {
             speed = 45;
             System.out.println("Truck moves at " + speed + " km/h");
         }
    
         @Override
         void stop() {
             speed = 0;
             System.out.println("Truck stops");
         }
     }
    
     class Bus extends Vehicle {
         Bus() {
             super(40);
         }
    
         @Override
         void move() {
             speed = 50;
             System.out.println("Bus moves at " + speed + " km/h");
         }
    
         @Override
         void stop() {
             speed = 0;
             System.out.println("Bus stops");
         }
     }
    

    객체 지향 설계 원칙 (SOLID)

    단일 책임의 원칙 (Single Responsibility Principle)

  • 클래스는 단 하나의 책임(기능)을 가져야 함
  • 한 클래스가 여러 개의 책임을 수행할 경우 내부 메서드 간 결합도가 높아질 가능성 존재
  • 응집도를 높이고 결합도를 낮추기 위해 책임을 분리

개방-폐쇄 원칙 (Open-Closed Principle)

  • 확장에는 열려있고 변경에는 닫혀 있어야 함
  • 기존 코드를 수정하지 않고 기능을 추가할 수 있도록 설계
  • 자주 변경되는 부분을 추상화하여 유지보수성과 재사용성을 높이는 것이 핵심

리스코프 치환 원칙 (Liskov Substitution Principle)

  • 자식 객체는 부모 객체를 완전히 대체 가능
  • 상속 관계에서 부모 클래스를 호출할 때 자식 클래스가 부모의 동작을 유지

인터페이스 분리 원칙 (Interface Segregation Principle)

  • 객체는 자신이 사용하지 않는 메서드에 의존하지 않음
  • 인터페이스를 범용적으로 설계하면 불필요한 기능을 구현해야 하는 상황이 발생 가능
  • 클라이언트의 목적과 용도에 맞게 인터페이스를 분리

의존관계 역전 원칙 (Dependency Inversion Principle)

  • 상속 관계로 이루어진 모듈을 사용할 때 하위 모듈의 인스턴스를 직접 생성하지 않고 상위 인터페이스 타입을 사용해야 하는 원칙
  • 하위 모듈을 직접 사용하면 해당 모듈이 변경될 때마다 상위 모듈도 자주 수정해야 하는 문제가 발생

제어의 역전(Inversion of Control, IoC)

프로그램의 제어 흐름을 직접 관리하는 것이 아니라 외부에서 관리하도록 하는 개념

소프트웨어에서의 IoC

  • 강한 결합도
    • 객체 간의 강한 결합도는 하나의 객체가 변경될 때 다른 객체에도 영향을 미치는 상황을 의미
    • 개발자가 객체를 직접 생성하고 관리하기 때문에 객체를 변경할 경우 사이드 이펙트가 발생할 가능성 증가
    • 객체가 서로 밀집하게 연결되어 있어 객체를 분리해서 재사용하기 어려움
  • 유연성 부족
    • 소프트웨어 개발에서 유연성이라는 것은 코드를 쉽게 변경하거나 확장할 수 있는 능력을 의미
    • 직접 객체를 관리할 시 특정 서비스나 컴포넌트의 구현이 변경될 경우 관리하고 있는 모든 객체들이 영향을 받게 됨
  • 테스트의 어려움
    • 객체를 직접 제어해서 의존성을 관리하기 때문에 테스트 시 의존성을 모의 객체나 스텁으로 교체하기 어려울 수 있음
    • 테스트를 위해 의존성을 분리하는 과정이 까다로움

Spring IoC 컨테이너와 Bean

  • IoC 컨테이너(Inversion of Control Container)
    • 애플리케이션에서 필요한 객체(Bean)를 생성하고 관리하는 역할을 수행
    • 설정 파일 또는 어노테이션 기반으로 Bean을 관리하며 의존성을 자동으로 주입 (Dependency Injection)
    • Bean 팩토리(BeanFactory): Bean의 생명 주기를 관리하는 핵심 컴포넌트
  • Bean(빈)
    • Spring에서 생성 및 관리하는 자바 객체
    • IoC 컨테이너에 의해 자동으로 객체를 생성하고 의존성을 주입받음
    • 싱글톤(Singleton) 패턴을 기본적으로 사용하여 객체 인스턴스를 한 개만 유지
      • 싱글톤 : 클래스의 인스턴스가 딱 1개만 생성되는 것을 보장하는 디자인 패턴
      • 불필요한 객체 생성을 방지하고 메모리 사용을 최적화
  • 빈 팩토리(BeanFactory)
    • 빈(Bean)을 등록하고 객체 생성, 조회, 호출 등의 다양한 기능을 담당
    • 빈 팩토리(BeanFactory)는 빈의 정의는 즉시 로딩해 오지만 빈(Bean)이 사용되기 전 까지는 인스턴스화 하지 않음
    • getBean() 메소드를 통해 호출되면 빈 팩토리(BeanFactory)는 의존성 주입을 통해 빈(Bean)을 인스턴스화 하고 빈(Bean)의 특성을 설정하기 시작
      • 해당 시점부터 빈(Bean)의 생명이 시작
  • 애플리케이션 컨텍스트(ApplicationContext)
    • 애플리케이션 컨텍스트(ApplicationContext)는 빈 팩토리(BeanFactory)를 상속해 확장한 컨테이너
    • 역할은 빈 팩토리와 동일하며 추가로 스프링이 제공하는 여러 부가 기능을 제공
      • Transaction 관리, AOP 처리 등
    • 애플리케이션 컨텍스트는 빈 팩토리와는 달리 초기화 시점에 빈(Bean)을 생성해 두기 때문에 빈(Bean)이 필요할 때 즉시 사용 가능

애플리케이션 컨텍스트의 시작과 종료 과정

애플리케이션 컨텍스트의 실행 과정

  1. 스프링 부트스트랩 클래스의 main() 메소드 호출
    • SpringApplication 클래스의 run() 메소드가 호출
    • run() 메소드는 애플리케이션 컨텍스트를 생성하고 초기화
  2. 애플리케이션 컨텍스트 초기화
    • 빈 객체 생성 및 의존성 주입
    • 빈 객체의 초기화 메소드 호출
    • 애플리케이션 설정 및 환경 설정 로드
  3. 애플리케이션 컨텍스트 시작
    • 컨테이너에 등록된 모든 빈 객체가 사용 가능
    • 애플리케이션 서버가 시작되고 요청을 처리할 준비

애플리케이션 컨텍스트 종료 시점

  • 애플리케이션 서버가 종료될 때
  • SpringApplication 클래스의 close() 메소드 호출
  • ApplicationContext 인터페이스의 close() 메소드 호출

애플리케이션 컨텍스트 종료 시 수행 작업

  • 빈 객체 소멸
  • 컨테이너 리소스 해제

빈을 스프링 컨테이너에 등록하는 방법

  1. @Bean 사용 설정 클래스에 @Configuration을 적용하고 내부 메서드에 @Bean 어노테이션을 적용하여 Bean을 등록

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    
     @Configuration
     public class AppConfig {
         @Bean
         public MyService myService() {
             return new MyServiceImpl();
         }
    
         @Bean
         public MyRepository myRepository() {
             return new MyRepositoryImpl();
         }
     }
    
  2. @Component 사용

    • @Component가 적용된 클래스는 자동으로 Bean으로 등록
    • @Controller: 요청을 처리하는 컨트롤러 클래스
    • @Service: 비즈니스 로직을 수행하는 서비스 클래스
    • @Repository: 데이터베이스 접근을 담당하는 클래스
    • @Configuration: Bean 등록을 위한 설정 클래스
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    
     @Controller
     public class MyController {
         @GetMapping("/greeting")
         public String greeting() {
             return "Hello, World!";
         }
     }
    
     @Service
     public class MyService {
         public String provideService() {
             return "Service has been provided";
         }
     }
    
     @Repository
     public class MyRepository {
         public String getData() {
             return "Data from database";
         }
     }
    

의존성 주입(Dependency Injection, DI)

의존성 (Dependency)

한 객체가 다른 객체를 생성하거나 메서드를 호출할 때 의존성이 발생
의존성 주입(DI)은 이러한 의존성을 외부에서 주입하여 객체 간의 결합도를 낮추는 기법

결합도 (Coupling)

객체 간의 의존성 정도를 의미

  • 강한 결합 (Tight Coupling): 객체가 직접 다른 객체를 생성하는 방식
  • 느슨한 결합 (Loose Coupling): 인터페이스를 통해 객체 간 의존성을 최소화하여 변경에 유연하게 대응 가능

의존성 주입 (DI)

  • 객체의 생성 및 의존성 주입을 외부에서 수행하는 방식
  • Spring IoC 컨테이너는 DI를 통해 객체를 자동으로 관리
  • DI를 적용하면 클래스 간 결합도를 낮추고 코드의 유연성을 향상

DI의 장점

  • 클래스 간의 결합도를 낮춤
    • 유지보수성 향상
  • 테스트가 용이
    • Mock 객체를 활용한 단위 테스트 가능
  • 코드의 재사용성 증가
    • 동일한 인터페이스를 구현한 다양한 클래스 사용 가능

의존성 주입 방법

  1. 생성자 주입 (Constructor Injection)
    • 생성자를 통해 의존성을 주입하는 방식
    • 불변성이 보장되고 테스트가 용이
  2. 수정자 주입 (Setter)
    • Setter 메서드를 통해 의존성을 주입하는 방식
    • 선택적 의존성 주입이 가능
    • 자바빈 프로퍼티 (JavaBean Property) 규약을 따르는 방식
      • Java에서는 객체의 필드 값을 직접 변경하지 않고 set/get 메서드를 통해 값을 읽고 수정하는 규칙을 정의
      • 자바빈 프로퍼티 규약이라고 하며, 수정자(setter) 메서드를 사용하여 값을 변경하는 방식
  3. 필드 주입 (Field Injection)
    • 필드 선언부에 직접 @Autowired를 적용하는 방식
    • 코드가 간결하지만 테스트가 어렵고 의존성 추적이 어려움
주입 방식장점단점사용 추천
생성자 주입객체 불변성 보장, 순환 참조 감지 가능, 가장 권장되는 방식모든 필드가 필요하지 않을 경우 불필요한 코드 증가 가능스프링이 가장 추천하는 방식
수정자 주입선택적 의존 관계 설정 가능, 객체 생성 후 변경 가능public 메서드 사용으로 인해 외부에서 객체 변경 가능필요에 따라 선택적 의존성을 사용할 경우
필드 주입코드가 간결테스트 어려움, 결합도 증가, 유지보수 어려움권장되지 않음

관점 지향 프로그래밍(Aspect-Oriented-Programming, AOP)

핵심 관심사와 횡단 관심사를 분리하여 프로그램을 구성하는 기법
객체 지향 방식으로도 해결되지 않는 중복 코드 문제를 해결하는 방식
공통 기능(횡단 관심사)을 분리하여 필요할 때만 핵심 기능에 적용

  • 핵심 관심사와 횡단 관심사를 분리하여 관리하면 유지보수성과 확장성이 향상
    • 핵심 관심사 (Core Concerns)
      • 개별 모듈이 가지는 독특한 기능
    • 횡단 관심사 (Cross-cutting Concerns)
      • 여러 모듈에서 공통적으로 사용되는 기능

AOP의 장점

  1. 유지보수성 향상
    • 핵심 비즈니스 로직에서 반복되는 부가 기능을 분리함으로써 코드의 가독성과 유지보수성이 향상
    • 수정이 필요할 때 중복된 코드를 하나의 모듈에서 변경 가능
  2. 코드 재사용성 증가
    • 횡단 관심사를 모듈화하면 같은 기능을 여러 곳에서 재사용 가능
  3. 테스트 용이성
    • 핵심 로직과 보조 기능이 분리되어 단위 테스트 수행이 쉬워짐
    • 단위 테스트 실행 속도 향상
  4. 에러 추적과 디버깅 용이성
    • 문제가 발생했을 때 오류의 원인을 추적하기 쉬움
  5. 시스템의 확장성 향상
    • 공통 관심사를 모듈화하여 적용하면 새로운 기능을 쉽게 추가 가능
    • 시스템 확장 시 핵심 로직에 미치는 영향을 최소화
  6. 명확한 책임 분리
    • 각 모듈이나 컴포넌트가 명확한 책임을 가지도록 구성 가능

간단 용어 설명

  • 애스팩트 (Aspect)
    • 핵심 기능에서 분리한 부가 기능과, 이를 어디에 적용할 지를 정의한 것
    • 횡단관심사를 구현하는 코드를 캡슐화하는 역할
    • 어드바이스와 포인트컷을 포함
  • 어드바이스 (Advice)
    • 애스팩트(Aspect)에서 정의되는 메소드
    • 횡단관심사를 구현하는 실제 코드를 담고 있음
    • Before, After, Around 등 다양한 종류가 존재
  • 조인포인트 (Join Point)
    • 애스팩트(Aspect)가 적용될 수 있는 특정 지점
    • 메서드 실행, 필드 접근, 객체 생성 등 다양한 시점에 발생
    • AOP 프레임워크에서 제공
  • 포인트컷(Pointcut)
    • 조인포인트 중에서 애스팩트가 실제로 적용될 조건을 정의
    • 필터링하여 애스팩트 적용 위치를 결정
    • execution, within, this 등 다양한 표현식을 사용 가능

AOP의 핵심 개념

  • 공통 기능(트랜잭션, 로깅, 보안 등)을 핵심 비즈니스 로직에서 분리
  • 객체 지향 프로그래밍(OOP)의 한계를 보완하여 중복된 부가 기능을 효과적으로 관리

PSA(Portable Service Abstraction)

PSA(Portable Service Abstraction, 일관성 있는 서비스 추상화)는 특정 기술에 종속되지 않고 서비스의 기능을 일관된 방식으로 유지하여 유연하게 사용할 수 있도록 하는 개념

PSA의 특징

  • 환경과 기술의 변화와 관계없이 동일한 방식으로 기술을 사용 가능
  • 특정 서비스에 대한 접근 방식을 표준화하여, 다양한 환경에서도 유연하게 적용 가능

PSA와 트랜잭션

트랜잭션은 쪼갤 수 없는 업무 처리의 최소 단위를 의미

트랜잭션의 4가지 특징(ACID)

  • 원자성(Atomicity)
    • 트랜잭션 실행이 완료되거나, 실패 시 이전 상태로 되돌아가야 함
  • 일관성(Consistency)
    • 트랜잭션이 실행되면 항상 데이터의 일관성이 유지
  • 독립성(Isolation)
    • 둘 이상의 트랜잭션이 동시에 실행될 경우 서로 독립적으로 처리
  • 지속성(Durability)
    • 트랜잭션이 성공적으로 완료되면 그 결과가 영구적으로 저장

Spring PSA

  • 스프링은 원자성, 일관성, 독립성, 지속성(ACID) 을 보장하기 위해 PlatformTransactionManager 인터페이스를 제공
    • PlatformTransactionManager를 통해 JDBC, JTA, Hibernate 등의 트랜잭션 관리 기술을 PSA로 추상화하여 제공

Spring JPA

관계형 데이터베이스를 객체 방식(ORM)으로 다룰 수 있도록 하는 인터페이스

JPA의 주요 특징

JPA를 사용함으로써 개발자는 객체 지향적이고 데이터베이스에 독립적인 방식으로 애플리케이션을 개발 가능

  1. 객체-관계 매핑(ORM)
    • JPA는 객체와 관계형 데이터베이스 테이블 사이의 매핑을 제공
    • 개발자가 SQL을 직접 다루지 않고 객체를 사용하여 데이터 작업 가능
  2. 데이터베이스 독립성
    • 특정 데이터베이스 SQL에 종속되지 않고 다양한 데이터베이스 시스템에서 사용 가능
  3. 추상화
    • 데이터 접근 로직을 추상화하여 SQL 쿼리를 직접 작성하지 않고 객체 지향 방식으로 데이터 조작 가능
  4. 트랜잭션 관리
    • JPA는 트랜잭션을 관리하며 엔티티의 일관성을 유지

Spring Boot

단독 실행이 가능한(Spring Boot Application) 스프링 기반 애플리케이션을 쉽게 개발할 수 있도록 도와주는 프레임워크

Spring Boot 특징

  1. 단독 실행 가능한 애플리케이션 생성
    • Tomcat, Jetty, Undertow 같은 내장 서버를 포함하고 있어 별도 설정 없이 실행 가능
    • WAR 파일을 이용한 배포가 필요 없음
  2. 간편한 의존성 관리
    • starter dependencies를 통해 필요한 의존성을 간단하게 추가 가능
  3. 자동 설정(Auto Configuration)
    • Spring 및 서드파티 라이브러리를 자동으로 구성하여 개발자의 설정 부담 감소
  4. 운영 환경 지원
    • 애플리케이션 모니터링, 메트릭 수집, 헬스 체크 등 다양한 운영 관련 기능 제공
  5. XML 설정 없이 코드 기반 구성
    • XML 설정이 필요 없으며, 대부분의 설정을 자바 코드 기반으로 처리

Spring Boot 확장 기능

모듈설명
Spring Boot WebSpring MVC 기반 웹 애플리케이션을 쉽게 개발할 수 있도록 지원
Spring Boot DataJPA, JDBC, NoSQL 등 데이터 액세스 관련 기능 지원
Spring Boot Security보안 관련 기능(인증, 인가, OAuth2 등) 제공
Spring Boot Actuator애플리케이션 모니터링 및 관리 기능 제공
Spring Boot Batch대량 데이터 처리 및 배치 작업 지원

Starter (스타터)

프로젝트에서 필요한 라이브러리를 자동으로 관리하고 설정해주는 기능

Starter의 장점

  • 편의성: 의존성을 개별적으로 추가할 필요 없이 하나의 스타터만 추가
  • 일관성: 프로젝트 내 라이브러리 버전이 일관되게 관리
  • 자동 구성: 추가된 스타터를 기반으로 설정이 자동 적용

개발 환경 구축

Java & Eclipse 설치하기

Java 설치

  1. Azul Zulu OpenJDK 17 다운로드
  2. 설치 파일 실행 및 진행
  3. 환경 변수 설정
    • 시스템 환경 변수에 JAVA_HOME 추가 (JDK 설치 경로 지정)
    • Path 변수에 %JAVA_HOME%\bin 추가
  4. 설치 확인
    • 명령 프롬프트에서 java -version 실행하여 설치 확인

STS(Spring Tool Suite) for Eclipse 설치

  1. STS 다운로드
  2. 파일 압축 해제
  3. 압축 해제된 폴더 내 STS 실행 파일 실행
  4. 워크스페이스 선택
  5. STS 설정
    • Window > Preferences(macOS의 경우 STS > Preferences) 메뉴 선택
    • Java > Installed JREs에서 설치한 Java 17 선택 또는 추가
  6. (선택사항) 인코딩 설정
    • General > Workspace에서 Text file encodingUTF-8로 변경
  7. (선택사항) 서버 설정
    • Servers 뷰에서 새 서버 추가

Lombok 설치

  1. Lombok 설치
  2. 프로젝트 설정
    • 프로젝트 우클릭 → Properties > Java Compiler > Annotation Processing
    • Enable annotation processing 체크 후 Apply and Close

Mustache 설치

  • Eclipse 설정 변경
    • Window > Preferences → Content Types 검색 후 HTML 선택
    • File associations → Add... 클릭 후 *.mustache 입력
    • Apply and Close 저장

Spring initializr를 이용해보기

Spring Initializr

Spring Initializr는 스프링 프로젝트를 쉽게 생성할 수 있는 도구
실행 환경과 필요한 라이브러리를 포함하여 템플릿을 제공

Spring Boot 프로젝트 구조 이해하기

src/main/java

Java 클래스, 인터페이스 등이 위치하는 디렉터리
@SpringBootApplication 어노테이션이 포함된 main 메서드가 존재

SpringBootApplication의 주요 어노테이션

  • @SpringBootConfiguration: Bean을 정의하는 역할
  • @EnableAutoConfiguration: Spring Boot 자동 설정 활성화
  • @ComponentScan: 해당 패키지 및 하위 패키지를 스캔하여 Bean 등록

src/main/resources

HTML, CSS, JavaScript, 환경 변수 등을 저장하는 공간

templates

HTML 템플릿 파일 저장 디렉터리
Java 코드와 함께 동적으로 HTML 페이지를 생성할 때 사용

static

CSS, JavaScript, 이미지 파일 저장 디렉터리
정적인 리소스를 제공

application.yml / application.properties

스프링 애플리케이션의 환경 설정 파일
데이터베이스 설정, 포트 번호 등 프로젝트의 주요 환경 변수 지정 YAML 형식 사용 가능 (JSON, XML보다 가독성이 높음)

src/test/java

테스트 코드 작성 공간
JUnit 등의 테스트 프레임워크를 사용하여 단위 테스트 수행 가능

build.gradle

Gradle 환경 설정 파일
프로젝트에 필요한 플러그인, 라이브러리(Dependencies) 등을 관리
dependencies 블록에서 Spring Boot Starter 및 추가 라이브러리를 정의

1
2
3
4
5
6
7
8
dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    runtimeOnly 'com.h2database:h2'
    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

Postman 및 IntelliJ로 프로젝트 열기

Postman

API 개발 및 테스트를 위한 HTTP 클라이언트 프로그램
API 요청을 직접 만들어 실행할 수 있으며 개발자 간 협업 및 디버깅에 유용

Postman의 주요 기능

  • 다양한 HTTP 요청(GET, POST, PUT, DELETE 등)을 테스트 가능
  • JSON, XML 등 다양한 응답 데이터 포맷을 확인 가능
  • API 테스트 자동화 및 요청 저장 기능 지원

Postman을 활용한 API 테스트

  1. Postman 실행 후 New Request 생성
  2. 요청 방식을 GET으로 설정
  3. URL 입력: http://localhost:8080
  4. Send 버튼 클릭하여 서버 응답 확인

Spring MVC / Data

MVC 디자인 패턴

디자인 패턴(Design Pattern)

공통적으로 발생할 수 있는 문제를 해결하기 위한 재사용 가능한 방법
과거의 소프트웨어 개발 과정에서 발견된 설계 노하우를 정형화하여 규약으로 정리한 것

MVC (Model-View-Controller)

model-view-controller-light-blue

애플리케이션을 Model, View, Controller 3가지 역할로 구분하여 개발하는 방법론
비즈니스 로직과 UI를 분리하여 코드 중복을 줄이고 협업과 유지보수가 용이

  • Model (모델)
    • 데이터 처리 영역
    • DB와 연동하여 데이터를 저장/조회하며 데이터를 다루는 로직을 포함
  • View (뷰)
    • 사용자에게 데이터를 출력하는 화면(UI)
    • 모델, 컨트롤러와의 의존성을 최소화
  • Controller (컨트롤러)
    • 사용자의 입력을 받아 모델과 뷰 사이에서 데이터 흐름을 조절
    • @Controller 어노테이션을 통해 스프링 컨트롤러 역할 수행

Spring MVC

  • Spring에서 제공하는 웹 모듈
  • 프론트 컨트롤러 패턴 기반으로 동작
  • 모든 사용자 요청을 DispatcherServlet이 받아 처리하고 적절한 컨트롤러에 전달
  • HTML 응답뿐만 아니라 JSON, REST API 응답까지 다양한 응답을 지원

DispatcherServlet의 동작 과정

  1. 클라이언트 요청 → DispatcherServlet이 받음
  2. 요청 정보를 기반으로 HandlerMapping이 적절한 컨트롤러 검색
  3. HandlerAdapter가 해당 컨트롤러의 메서드를 실행
  4. 컨트롤러가 비즈니스 로직 수행 후 Model 데이터 반환
  5. ViewResolver가 적절한 View를 찾아 데이터와 함께 렌더링
  6. 완성된 View를 DispatcherServlet이 클라이언트에게 응답

Spring DataBase Access

영속성(Persistence)

데이터를 생성한 프로그램의 실행이 종료되더라도 사라지지 않는 데이터의 특성
파일 시스템, 관계형 데이터베이스(RDB) 등을 통해 구현 가능
Java에서는 JDBC(Java Database Connectivity)를 통해 데이터베이스 프로그래밍이 이루어짐

Spring DataBase 프로그래밍 유형

JDBC (Java Database Connectivity)

  • 자바에서 데이터베이스에 접근할 수 있도록 하는 자바 API
  • 데이터베이스와 애플리케이션을 연결하여 SQL 쿼리를 실행하고 결과를 반환
  • DB 드라이버 클래스를 사용하여 서로 다른 데이터베이스를 동일한 방식으로 조작 가능

JDBC Template

  • JDBC 사용을 단순화하고 일반적인 오류를 방지하는 클래스
  • 순수 JDBC에 비해 편리하고 코드가 단축
  • SQL 실행 및 결과 매핑이 용이

MyBatis

  • XML 또는 어노테이션을 사용하여 SQL 쿼리를 매핑하고 실행할 수 있도록 지원하는 프레임워크
  • Connection 관리를 자동화하여 반복적인 JDBC 작업 감소
  • 복잡한 쿼리를 다이나믹하게 작성 가능하며 유지보수가 용이

JPA

  • 객체와 관계형 데이터베이스 간의 매핑을 제공하는 ORM(Object Relational Mapping) 기술
  • 표준 인터페이스로 Hibernate, EclipseLink 같은 ORM 프레임워크를 통해 구현
  • 개발자가 SQL을 직접 작성하지 않아도 객체를 저장하고 검색 가능

ORM

객체 지향 프로그래밍과 관계형 데이터베이스 간의 패러다임 불일치를 해결하는 기술
객체(Entity)와 데이터베이스 테이블을 자동 매핑하여 SQL을 직접 작성하지 않아도 데이터 조작 가능

ORM 주요 개념

  • Entity: 데이터베이스 테이블과 매핑되는 클래스
  • Repository: 데이터 액세스를 위한 인터페이스

ORM 장점

  1. 객체 중심 데이터 관리
    • 일관된 프로그램 구조 유지
  2. SQL 없이 데이터 조작 가능
    • 코드 가독성 및 유지보수 용이
  3. 자동 SQL 생성 및 트랜잭션 관리
    • 개발자 부담 감소
  4. 데이터베이스에 대한 종속성 감소 가능

ORM 단점

  1. 학습 난이도가 높고 진입 장벽이 있음
  2. 모든 Query를 ORM이 처리할 수 없음
    • 복잡한 쿼리는 JPQL, QueryDSL 필요
  3. 잘못된 ORM 사용 시 성능 저하 가능

Spring Data JPA(Java Persistence API)

JPA(Java Persistence API)

JPA는 Java 어플리케이션에서 관계형 데이터베이스를 객체-관계 방식(ORM)으로 사용할 수 있도록 만든 인터페이스
JDBC API를 활용하여 SQL을 실행하고 결과를 반환하는 과정을 자동화하여 개발자의 부담 감소

JPA 특징

  • JPA는 인터페이스이기 때문에 구현체가 없으며 다양한 ORM 프레임워크에서 구현하여 사용 가능
  • Hibernate가 대표적인 JPA 구현체이며 객체와 관계형 데이터베이스 간의 매핑을 자동으로 처리
  • SQL을 직접 작성하지 않아도 되며 객체를 저장하고 검색하는 메서드를 제공

JPA를 사용하는 이유

  1. 생산성 향상
    • 메서드 호출만으로 쿼리를 실행 가능
    • JPA가 SELECT SQL을 자동으로 생성하여 실행
  2. 유지보수 용이
    • 테이블 칼럼 변경 시 SQL을 수정할 필요 없음
    • JPA가 변경된 값을 자동으로 반영
  3. 패러다임 불일치 해결
    • 객체 모델과 관계형 데이터베이스 모델 간의 차이를 자동으로 해결

JPA의 데이터 접근 추상화 및 벤더 독립성

  • JPA는 데이터베이스 접근 계층을 추상화하여 특정 DBMS에 종속되지 않도록 설계
  • 여러 DB 벤더(MySQL, Oracle, MS-SQL 등)마다 다른 SQL을 사용하지만 JPA는 특정 데이터베이스 기술에 종속되지 않도록 해줌
  • 설정 변경만으로 손쉽게 데이터베이스를 변경 가능

JPA의 표준화

  • JPA는 자바 진영의 ORM 표준 기술
  • 표준을 사용하면 다른 구현 기술(Hibernate, EclipseLink 등)로 쉽게 변경 가능

JPA 주요 기능 정리

  • 플러시(Flush)
    • em.flush() 호출 시 영속성 컨텍스트의 변경 내용을 DB에 반영
    • 트랜잭션 커밋 시 자동으로 flush 발생
    • JPQL 실행 시 자동으로 flush 호출
  • JPQL 실행
    • JPQL(Java Persistence Query Language)은 SQL을 객체 지향적으로 변환한 쿼리 언어
    • 엔티티를 대상으로 SQL과 유사한 문법으로 데이터를 조회 가능
    • JPQL 실행 시 자동으로 flush 발생하여 변경 사항이 반영된 후 쿼리 실행

영속성 컨텍스트(Persistence Context)

영속성 컨텍스트

엔티티를 영구 저장하는 환경
어플리케이션과 DB 사이에서 객체를 관리하는 가상의 DB 역할
엔티티 매니저(EntityManager)를 통해 관리되며 엔티티를 저장·조회할 때 영속성 컨텍스트가 개입

영속성 컨텍스트의 주요 기능

  1. 1차 캐시
    • 영속성 컨텍스트는 내부적으로 1차 캐시를 보유
    • ID(Primary Key)를 키 값으로 하고 엔티티를 값으로 가지는 Map 구조
    • 동일한 엔티티를 반복 조회 시 DB를 조회하지 않고 캐시에서 바로 가져옴
      • 쿼리 성능 향상
  2. 쓰기 지연 (SQL 저장소)
    • 한 트랜잭션 내에서 발생하는 SQL을 쓰기 지연 SQL 저장소에 저장 후 한 번에 DB에 반영
    • 트랜잭션이 커밋되는 순간 한꺼번에 SQL 실행
    • DB 커넥션 유지 시간을 줄이고 성능 최적화 가능
  3. 변경 감지 (Dirty Checking)
    • 1차 캐시에 저장된 엔티티의 상태를 스냅샷과 비교하여 변경된 필드만 자동 감지
    • 변경이 감지되면 flush() 시점에 자동으로 UPDATE SQL 실행
  4. 지연 로딩 (Lazy Loading)
    • 연관 관계가 매핑된 엔티티를 조회할 때, 처음부터 모든 데이터를 가져오지 않고 실제 사용할 때 필요한 데이터를 가져오는 기능
    • 프록시 객체를 반환하여 성능 최적화 가능

엔티티 생명 주기

상태설명
비영속 (New, Transient)엔티티가 영속성 컨텍스트와 관계없는 상태
영속 (Managed)em.persist(entity) 호출 후 영속성 컨텍스트에 저장된 상태
준영속 (Detached)em.detach(entity)를 호출하여 영속성 컨텍스트에서 분리된 상태
삭제 (Removed)em.remove(entity)를 호출하여 엔티티를 삭제한 상태

Spring Data JPA

Spring Data

데이터 저장소(DB)에 대한 특성을 유지하며 데이터 액세스 방법에 대해 친숙하고 익숙한 접근 방법을 제공하는 Spring 기반 프로그래밍 모델

  • 데이터 액세스, 관계형 및 비관계형 데이터베이스, 클라우드 기반 데이터 서비스를 쉽게 사용할 수 있도록 지원
  • 특정 데이터 저장소(RDBMS 등)를 사용하더라도 일관적인 방식의 프로그래밍으로 데이터에 접근 가능

Spring Data JPA Spring Data JPA 공식 사이트는 JPA 기반 저장소를 쉽게 구현할 수 있도록 지원하며 데이터 액세스 기술을 활용한 스프링 기반 애플리케이션을 쉽게 구축 가능

  • Spring Framework에서 제공하는 모듈 중 하나로 개발자가 더 쉽게 JPA를 사용할 수 있도록 도와줌
  • JPA를 한 단계 추상화한 Repository 인터페이스를 제공하여 데이터 액세스를 더욱 간결하게 처리할 수 있음
  • Repository 내부에서 JPA가 활용

Spring Data JPA의 구조

Spring Data JPA는 애플리케이션과 데이터베이스를 연결하는 계층적인 구조

  • Application: 애플리케이션에서 직접 Spring Data JPA를 활용
  • Spring Data JPA: JPA 기반의 데이터 액세스를 간편하게 처리
  • JPA (Java Persistence API): 객체-관계형 매핑(ORM) 표준 인터페이스
  • Hibernate (JPA 구현체): JPA를 지원하는 가장 널리 사용되는 ORM 프레임워크
  • JDBC API: 데이터베이스와의 연결 및 SQL 실행을 담당
  • Relational Database (RDBMS): 실제 데이터를 저장하는 관계형 데이터베이스

DDL (Data-Definition Language)

데이터 정의 언어로 데이터베이스의 구조를 정의하고 관리하는데 사용되는 SQL 명령어
ddl-auto는 JPA에서 사용되는 설정 중 하나로 Hibernate를 포함한 JPA 구현체가 데이터베이스의 스키마를 어떻게 처리할지 지정

ddl-auto 속성의 종류

속성설명
create엔티티로 등록된 클래스와 매핑되는 테이블을 자동으로 생성
create-dropcreate와 동일하며 애플리케이션 종료 시 생성된 테이블을 자동으로 삭제
update매핑된 테이블이 없다면 자동으로 생성, 테이블과 엔티티 구조가 다를 경우 엔티티 기준으로 변경(업데이트)
validate매핑된 테이블들의 존재 유무만 확인
none (default)아무 작업도 수행하지 않음

엔티티 매핑

  • JPA를 이용하여 데이터베이스의 테이블과 상호작용(CRUD) 하기 위해 먼저 수행해야 할 작업은 DB Table ⇄ Entity Class 간의 매핑
  • 엔티티 클래스를 생성하면 데이터베이스 테이블 구조가 자동으로 생성됨
  • application.ymlddl-auto 설정에 따라 테이블 생성이 결정

어노테이션

Lombok 라이브러리에서 제공하는 어노테이션

어노테이션설명
@NoArgsConstructor매개변수가 없는 기본 생성자를 자동 생성
@AllArgsConstructor모든 필드 값을 매개변수로 받는 생성자를 자동 생성
@Getter / @Setter필드에 대한 getter, setter 메서드를 자동 생성
  • JPA는 DB 값을 자바 객체로 직렬화할 때 Reflection을 이용
  • Reflection을 통해 클래스의 메소드, 필드 등에 접근 가능
  • 기본 생성자가 없으면 JPA에서 예외(InstantiationException) 발생

JPA 어노테이션

  • @Entity: 해당 클래스를 데이터베이스 테이블과 매핑
  • @Table: 특정 테이블에 매핑할 때 사용, 테이블 이름을 명시할 수 있음
  • @Id: PK(Primary Key) 설정을 위한 어노테이션
  • @GeneratedValue: 기본 키 생성 전략 지정

기본 키 생성 전략

  • GenerationType.IDENTITY: DB에서 AUTO_INCREMENT를 사용하여 기본 키를 생성
  • GenerationType.SEQUENCE: 데이터베이스 시퀀스를 사용하여 기본 키 할당
  • GenerationType.TABLE: 키 생성 테이블을 사용하여 기본 키를 할당
  • GenerationType.AUTO: 사용하는 데이터베이스에 따라 자동으로 적절한 방식 선택

필드와 컬럼 매핑

@Column 어노테이션

속성설명
name컬럼 이름을 명시적으로 지정
nullablenull 값 허용 여부 설정
unique중복 방지 컬럼 여부 설정
length문자열 길이 제한 (String 타입에서만 사용)

@Enumerated 어노테이션

Enum 타입설명장점단점
EnumType.ORDINALEnum의 순서(index) 값을 DB에 저장데이터베이스에 저장되는 데이터의 크기가 작음이미 저장된 enum의 순서는 변경 불가능
EnumType.STRINGEnum 이름을 DB에 문자열로 저장저장된 enum의 순서가 바뀌거나 추가되어도 안전ORDINAL 방식보다 저장되는 데이터 크기가 큼

기타 JPA 어노테이션

매핑 어노테이션설명
@Temporal날짜 타입을 java.util.Date 또는 java.util.Calendar로 매핑
@CreationTimestamp엔티티 생성 시 현재 시간 자동 기록
@UpdateTimestamp엔티티 업데이트 시 현재 시간 자동 기록
@Transient특정 필드를 데이터베이스에 매핑하지 않음
@Lob데이터베이스 BLOB, CLOB 타입을 매핑

연관 관계 매핑

엔티티 클래스 간의 관계를 만들어 주는 것
객체는 참조를 사용해 관계를 맺고 테이블은 외래 키를 사용해서 관계를 맺음

개념설명
다중성N:1, 1:N, 1:1, N:M 관계 정의 가능
연관 관계 주인mappedBy 속성을 사용하여 관계 주인 설정
방향객체는 단방향 또는 양방향 관계를 가질 수 있음
단방향 관계한쪽 엔티티만 다른 엔티티를 참조
양방향 관계서로 상대 엔티티를 참조하여 관계 설정

Repository 인터페이스

Spring Data JPA에서 제공하는 데이터베이스 접근을 위한 인터페이스

Repository설명
JpaRepository기본 CRUD + 페이징, 정렬 기능 포함
PagingAndSortingRepositoryCRUD + 페이징 및 정렬 기능 제공
CrudRepository기본적인 CRUD 기능 제공
QueryByExampleExecutor예제(Example)를 활용한 다양한 쿼리 실행

RestController를 활요한 웹 애플리케이션 개발

REST & REST API

REST

REST(Representational State Transfer)는 자원을 이름으로 구분하여 해당 자원의 상태를 주고받는 모든 것을 의미하는 웹 아키텍처 원칙

REST 구성 요소

  • 자원(Resource)
    • 서버에서 관리하는 모든 것 (ex. 이미지, 데이터, 문서 등)
  • 표현(Representation)
    • 자원을 표현하는 이름
  • 상태 전달(State Transfer)
    • 클라이언트가 요청할 때 자원의 상태를 전달 (JSON 또는 XML 형태)

REST 특징

  • 인터페이스 일관성(Uniform Interface)
    • 리소스에서 수행할 수 있는 작업의 일관된 인터페이스를 정의
    • GET, POST, PUT, DELETE 같은 HTTP Method 사용하여 구현
  • 무상태성(Stateless)
    • 작업을 위한 상태 정보를 따로 저장하거나 관리하지 않음
    • 세션 정보나 쿠키 정보를 서버에서 저장하지 않음
      • API 서버는 요청을 단순히 처리하는 역할
    • 서버의 부담 감소
      • 확장성(Scalability) 향상
  • 캐싱(Cacheable)
    • REST는 HTTP 웹 표준을 사용하므로 기존의 인프라를 그대로 활용 가능
    • HTTP 프로토콜에서 제공하는 Last-Modified 태그나 E-Tag를 이용하여 캐싱 지원
  • 서버-클라이언트 구조(Server-Client)
    • 클라이언트와 서버를 명확히 분리
    • 클라이언트와 서버가 분리되면 서버의 역할이 단순해지고 다양한 플랫폼에서 사용 가능
    • 같은 데이터라도 웹, 앱, 기타 여러 클라이언트에서 활용 가능
  • 계층형 구조(Layered System)
    • REST 서버는 다층(Layered) 구조로 설계 가능
    • 로드 밸런싱, 암호화, 프록시 등의 계층을 추가하여 구조상의 유연성을 제공
    • 클라이언트는 여러 계층을 인식하지 못한 채 서비스 사용 가능

REST 장점

  • 기존 HTTP 인프라 활용
    • REST API를 위한 별도의 인프라 구축이 필요 없음
  • 표준 활용
    • HTTP 프로토콜의 표준을 최대한 활용하여 추가적인 장점 제공
  • 플랫폼 독립성
    • HTTP 프로토콜 기반으로 모든 플랫폼에서 사용 가능
  • 클라이언트-서버 역할 분리
    • 서버와 클라이언트의 역할을 명확히 분리하여 유지보수 용이

REST 단점

  • 표준 정의 부족
    • REST는 표준 자체가 존재하지 않아 정의가 필요
  • HTTP Method 한계
    • GET, POST, PUT, DELETE 등 제한적인 메서드 사용
  • 테스트 및 디버깅 어려움
    • 브라우저 기반 테스트가 많으면 Header 값 수정이 어려워 테스트가 까다로움

REST API

API (Application Programming Interface)

프로그램이 상호작용할 수 있도록 돕는 매개체
개발자가 코드를 작성하는 방법을 표준화하여 개발 과정을 간소화 가능
요청 형식 및 응답 데이터의 형태를 정리한 일종의 규약

REST API

REST의 특징을 기반으로 서비스 API를 구현한 것
가 요청이 특정 정보를 요청하는지 요청 자체만으로 추론할 수 있도록 설계

REST API 설계 원칙

  1. 자원은 명사 사용
    • ex. http://www.example.com/emails
  2. 컬렉션(복수) 명사 사용
    • ex. http://www.example.com/books
  3. 고유한 객체는 ID 포함
    • ex. http://www.example.com/emails/:id
  4. 마지막에 슬래시 포함하지 않음
    • ex. http://www.example.com/pages
  5. 언더바 대신 하이픈(-) 사용
    • ex. http://www.example.com/user-profiles
  6. 조인된 결과를 URI에 포함하지 않음
    • ex. http://www.example.com/users/{id}/user-profiles
  7. URI에 파일 확장자 포함하지 않음
    • ex. http://www.example.com/documents
  8. 행위를 포함하지 않음(HTTP 메서드 사용)
    • ex. POST http://www.example.com/users

REST API 동작

서버는 클라이언트 요청에 대한 응답(view)을 제공하며 응답 데이터는 일반적으로 JSON 형식을 사용
JSON(JavaScript Object Notation)은 JavaScript 객체 문법을 활용한 데이터 구조 표현 방식으로 텍스트 기반의 데이터 교환 표준

REST API 요청・응답 과정

REST API 요청 및 응답 개요

a8d093_d5c49822facd4a30b72d032e0e83a425_mv2

REST API는 클라이언트가 HTTP 요청을 통해 데이터를 서버로 전송하고 서버는 해당 요청을 처리하여 응답을 반환하는 방식으로 동작
이 과정에서 응답 데이터는 일반적으로 JSON(JavaScript Object Notation) 형식으로 클라이언트에 전송

HTTP 요청 구조

  • 요청 라인(Request Line)
    • HTTP 요청의 시작 부분
      • HTTP Method: GET, POST, PUT, DELETE 등 요청의 의도를 담고 있는 HTTP 메서드
      • Request Target: 요청이 전달되는 URL (목표 주소)
      • HTTP Version: HTTP 버전 정보 (ex. HTTP/1.1, HTTP/2.0)
  • 헤더(Header)
    • HTTP 요청과 응답에 대한 추가 정보를 담고 있는 부분
      • Host: 요청 대상 서버의 도메인 주소 및 포트번호
      • User-Agent: 요청을 보낸 클라이언트의 프로그램 정보 (ex. 웹 브라우저, 모바일 앱 등)
      • Authorization: 인증이 필요한 경우 인증 토큰을 포함하여 서버로 전달
      • Cookie: 클라이언트가 서버에 저장한 쿠키 값을 전송
  • 본문(Body)
    • HTTP 요청에 포함되는 데이터가 담기는 부분 (필요한 경우에만 사용)
    • POST, PUT 요청에서 주로 사용되며 JSON, XML, Form 데이터 등의 형태로 전송 가능
    • GET 요청은 일반적으로 Body를 포함하지 않음

HTTP 응답 구조

  • 상태 라인(status Line)
    • HTTP 응답 메시지의 첫 번째 줄로 서버가 클라이언트에 반환하는 HTTP 상태 정보를 포함
    • 주요 요소
      • HTTP Version: 서버가 사용하는 HTTP 버전 정보
      • Status Code: 요청의 처리 결과를 나타내는 세 자리 숫자 코드
      • Status Text: 상태 코드에 대한 설명 (ex. “OK”, “Not Found”)
    상태 코드의미
    200 OK요청이 성공적으로 처리
    201 Created요청이 성공적으로 수행되었으며 새로운 리소스가 생성
    400 Bad Request잘못된 요청 (클라이언트 오류)
    401 Unauthorized인증되지 않은 요청
    403 Forbidden접근 권한이 없음
    404 Not Found요청한 리소스를 찾을 수 없음
    500 Internal Server Error서버 내부 오류 발생
  • 헤더(Header)
    • 응답에 대한 추가 정보를 포함
    • 주요 헤더 정보
      • Server: 응답을 제공하는 웹 서버의 정보
      • Location: 301, 302 상태 코드일 때, 리다이렉션할 URL 정보 제공
  • 본문(Body)
    • HTTP 응답에서 클라이언트에 전달할 데이터가 포함되는 부분
    • JSON, XML, HTML, 텍스트 등의 형식으로 데이터를 전송 가능능
    • 요청 처리 결과(ex. 데이터 목록, 생성된 객체 정보 등)가 포함

REST API 요청 및 응답 과정의 특징

  • 클라이언트가 요청을 보내면 서버가 응답을 반환하는 구조
  • 클라이언트와 서버 간 상태를 유지하지 않음(Stateless)
  • 요청과 응답은 일반적으로 JSON 형식을 사용하여 데이터 교환
  • HTTP 상태 코드로 요청의 성공 여부를 확인 가능
  • RESTful API를 사용하면 확장성과 재사용성이 높아짐

RESTful API를 활용한 효율적 통신

  • 클라이언트는 별도의 상태 유지 없이 서버의 리소스를 활용 가능
  • 서버는 클라이언트 요청에 따라 체계적으로 데이터를 제공하여 관리가 용이
  • RESTful API의 구조를 따르면 웹, 모바일, 데스크톱 환경에서도 일관된 API 사용이 가능

Service Layer

컨트롤러와 리포지토리 사이에 위치하는 계층으로 서버의 핵심 기능(비즈니스 로직)을 처리하는 계층
요청 데이터를 기반으로 실질적인 비즈니스 요구사항을 처리
비즈니스 로직을 분리 및 중앙화하여 유지보수성과 가독성을 향상시키고 개발 효율 향상

Service Layer의 주요 역할

  • Presentation Layer
    • Spring MVC를 통해 클라이언트의 요청을 처리하고 응답을 생성하는 역할
  • Service Layer
    • 비즈니스 로직을 처리하고 트랜잭션을 관리하는 역할
    • 도메인 모델과 상호작용하여 핵심 기능을 수행
    • 비즈니스 로직을 캡슐화하여 코드 재사용성을 향상
  • Data Access Layer
    • 데이터의 영속성을 관리하고 CRUD 작업을 수행
    • 데이터베이스와 상호작용하는 역할

역할 분리의 장점

  • 각 계층의 역할이 명확해지고 코드의 가독성과 유지보수성이 향상
  • 비즈니스 로직을 중앙화하여 다른 컨트롤러에서도 동일한 로직을 쉽게 재사용 가능
  • 서비스 레이어를 도입하면 트랜잭션 관리가 용이

@Transactional

트랜잭션(Transactions)

트랜잭션은 여러 단계로 구성된 작업을 논리적으로 묶어 하나의 논리적 단위로 처리하는 것을 의미
데이터베이스에서 트랜잭션을 사용하면 하나의 작업을 안전하게 처리하도록 보장 가능

@Transactional 속성

@Transactional 어노테이션은 클래스나 메서드 레벨에 적용 가능하며 다양한 속성을 지원

트랜잭션 주요 속성

  • Propagation (전파)
    • 기존 트랜잭션을 사용할지 새로운 트랜잭션을 시작할지를 결정
    • 기본적으로 존재하는 트랜잭션에 참여하거나 새로운 트랜잭션을 시작 가능
  • Isolation (격리 수준)
    • 데이터베이스의 기본 격리 수준을 따르거나 특정 격리 수준을 지정 가능
  • ReadOnly
    • 읽기 전용으로 설정하여 데이터 변경을 방지하고 성능을 최적화
  • rollbackFor/noRollbackFor
    • 특정 예외 발생 시 트랜잭션을 롤백하거나 롤백하지 않도록 설정 가능
  • Timeout
    • 트랜잭션의 최대 실행 시간을 초 단위로 설정, 초과 시 자동 롤백

트랜잭션 설정

Spring에서 @Transactional을 사용하려면 @EnableTransactionManagement 어노테이션을 클래스에 추가하고 트랜잭션 관리자를 빈으로 등록
일반적으로 DataSourceTransactionManagerJpaTransactionManager를 사용

1
2
3
4
5
6
7
8
9
10
11
12
13
@Configuration
@EnableTransactionManagement
public class AppConfig {
    @Bean
    public PlatformTransactionManager transactionManager() {
        return new DataSourceTransactionManager(dataSource());
    }

    @Bean
    public DataSource dataSource() {
        // 데이터베이스 연결정보 포함
    }
}

@Transactional의 장점

  • 선언적 트랜잭션 관리
    • 코드 내에서 트랜잭션 관리 로직을 분리하여 가독성과 유지보수성이 향상
  • 일관성 있는 데이터 처리
    • 모든 작업이 성공적으로 완료되거나 실패하면 이전 상태로 돌아감
  • 간편한 롤백 처리
    • 예외 발생 시 자동으로 트랜잭션이 롤백됨
  • 트랜잭션의 전체 및 격리 수준 제어
    • 다양한 트랜잭션 시나리오에 대응 가능

@Transactional 사용 시 주의점

  1. 프로세스 기반 동작
    • Spring의 선언적 트랜잭션 관리는 프로세스 기반으로 작동하며 같은 클래스 내부에서 @Transactional 메서드를 직접 호출할 경우 트랜잭션이 적용되지 않음
  2. 예외 처리
    • 기본적으로 @Transactional은 런타임 예외 발생 시 롤백을 수행하며 체크 예외에 대해서는 롤백이 발생하지 않음
    • 특정 예외에 대해 롤백을 원하면 rollbackFor 속성을 설정해야 함
  3. 트랜잭션 격리 레벨 선택
    • 높은 격리 수준을 설정하면 데드락 발생 가능성이 증가 가능
  4. 성능 최적화
    • @Transactional(readOnly = true)를 설정하면 트랜잭션 관리 오버헤드 감소 가능

트랜잭션의 ACID 특성

트랜잭션의 주요 특징은 ACID (Atomicity, Consistency, Isolation, Durability)

  • 원자성 (Atomicity)
    • 트랜잭션의 모든 연산은 원자 단위로 실행되어야 함
    • 모든 연산이 성공적으로 수행되거나 실패하면 전체 트랜잭션이 롤백됨
  • 일관성 (Consistency)
    • 트랜잭션 실행 후에도 데이터베이스의 무결성이 유지
  • 고립성 (Isolation)
    • 여러 트랜잭션이 동시에 실행될 때 서로 영향을 미치지 않고 독립적으로 실행될 수 있어야 함
  • 지속성 (Durability)
    • 트랜잭션이 성공적으로 완료된 경우 그 결과는 영구적으로 반영되어야 함

Spring에서 @Transactional을 이용한 트랜잭션 처리

  • DB와 관련된 트랜잭션이 필요한 서비스 클래스 혹은 메서드에 @Transactional 어노테이션을 적용
  • 클래스와 메서드 모두 @Transactional을 적용한 경우 메서드 레벨의 @Transactional이 우선 적용
  • @Transactional이 붙은 메서드는 포함하고 있는 작업 중 하나라도 실패하면 전체 작업을 취소

@Transactional 주요 속성

Propagation(전파) 속성

트랜잭션 동작 도중 다른 트랜잭션을 호출하는 상황에 선택

속성설명
PROPAGATION_MANDATORY특정 트랜잭션이 존재해야 실행 가능
PROPAGATION_NESTED기존 트랜잭션이 있는 경우 포함하여 실행
PROPAGATION_NEVER트랜잭션 실행 중이라면 예외 발생
PROPAGATION_NOT_SUPPORTED트랜잭션이 있는 경우 종료 후 실행
PROPAGATION_REQUIRED트랜잭션이 있으면 실행, 없으면 새 트랜잭션 시작
PROPAGATION_REQUIRES_NEW대상 자신만의 고유한 트랜잭션을 실행
PROPAGATION_SUPPORTS트랜잭션이 필요하지 않지만 존재하면 포함하여 실행

Isolation(격리) 레벨

데이터베이스 트랜잭션 간의 격리 수준을 설정

레벨설명
DEFAULTDB 기본 격리 수준 사용
SERIALIZABLE가장 높은 격리 수준, 성능 저하 가능
READ_UNCOMMITTED커밋되지 않은 데이터 읽기 허용
READ_COMMITTED커밋된 데이터만 읽기 허용
REPEATABLE_READ동일 필드 다중 접근 시 동일한 결과 보장

추가 속성

  • Read-Only 속성
    • @Transactional(readOnly = true) 설정 시 데이터 변경을 방지하고 성능을 최적화할 수 있음
  • Rollback for 특정 예외
    • 특정 예외가 발생했을 때만 강제로 롤백을 수행하도록 설정 가능
  • No-Rollback for 특정 예외
    • 특정 예외 발생 시 롤백을 수행하지 않도록 설정 가능
  • Timeout 속성
    • 트랜잭션 실행 시간이 초과될 경우 자동으로 롤백 처리

RestController

@RestController 개요

  • @RestController@Controller@ResponseBody를 포함하는 편리한 어노테이션
  • 모든 메서드에서 리턴되는 값이 MessageConverter에서 변환되어 HTTP Response Body에 자동으로 쓰여짐
  • RESTful API 개발에 주로 사용

@ResponseBody

  • HTTP 응답을 처리하는 메서드에 적용하여 해당 메서드의 반환값을 HTTP 응답 본문으로 사용하도록 지정
  • @RestController에 포함되어 있어 별도로 선언할 필요 없음

ResponseEntity

HTTP 상태 코드, 헤더, 본문을 포함하여 응답을 반환하는 클래스

RestController 동작 과정

  1. 클라이언트 요청
    • 클라이언트는 URI 형식으로 웹 서비스에 요청을 전송
  2. DispatcherServlet 인터셉트
    • DispatcherServlet이 요청을 인터셉트하고 적절한 핸들러(Controller)를 찾음
  3. Controller 처리
    • @RestController 내의 메서드에서 요청을 처리하고 JSON 데이터를 반환
  4. 응답 전송
    • DispatcherServlet이 JSON 데이터를 HTTP 응답으로 변환하여 클라이언트에게 전송

Global Exception

Global Exception Handle

Spring MVC에서 제공하는 기능으로 애플리케이션 전체에서 발생하는 예외를 중앙 집중식으로 처리할 수 있는 메커니즘
개별 컨트롤러에서 예외를 처리하는 대신 전역 예외 처리기를 사용하여 코드 중복을 줄이고 일관된 예외 처리를 구현 가능

Global Exception Handler의 주요 구성 요소

  • @ControllerAdvice / @RestControllerAdvice
    • 특정 클래스에서 발생하는 예외를 전역적으로 처리하도록 지정
    • 해당 어노테이션이 적용된 클래스는 모든 컨트롤러에서 발생한 예외를 감지하고 처리 가능
  • @ExceptionHandler
    • 특정 예외 유형을 처리하는 메서드에 적용
    • 해당 메서드는 예외가 발생하면 자동 호출되어 적절한 응답을 반환
  • HTTP 상태 코드와 예외 처리
    • 일반적인 HTTP 응답 상태 코드만으로는 클라이언트가 오류의 세부 사항을 알기 어려움
    • 전역 예외 처리기를 사용하면 오류 메시지와 함께 추가 정보를 응답에 포함 가능
    • 이를 통해 클라이언트가 오류의 원인을 정확히 파악하고 대응 가능

전역 예외 처리의 장점

  • 코드 중복 제거
    • 모든 컨트롤러에서 개별적으로 예외 처리를 할 필요 없이 중앙에서 일괄 관리 가능
    • 코드의 유지보수성이 향상
  • 일관된 예외 처리
    • 애플리케이션 전체에서 동일한 방식으로 예외를 처리하여 가독성과 협업 효율성 증가
  • 사용자 경험 향상
    • 적절한 오류 메시지와 HTTP 상태 코드를 제공하여 클라이언트가 오류를 쉽게 이해하고 대응할 수 있도록 도움
  • 보안 강화
    • 스택 트레이스 등의 민감한 정보를 노출하지 않도록 설정 가능
    • 악의적인 사용자로부터 시스템 보호에 도움
  • 로그 및 모니터링 향상
    • 예외 발생 시 로그를 자동으로 기록하고 모니터링 시스템에 전달 가능
    • 이를 통해 애플리케이션의 오류를 효과적으로 추적 및 분석 가능

커스텀 예외 클래스 개요

애플리케이션에서 특정 상황이나 오류를 명확하게 표현하고 처리하기 위해 정의
특정 조건에서 발생하는 예외를 개별 클래스로 관리하여 코드의 가독성과 유지보수성을 높이는 데 도움

커스텀 예외 클래스의 목적과 역할

  • 명확한 오류 표현
    • 커스텀 예외 클래스는 특정 오류 상황을 명확하게 표현하는 역할 수행
  • 비즈니스 로직 분리
    • 커스텀 예외를 사용하면 비즈니스 로직에서 다양한 오류 상황을 예외 처리기에서 명확하게 구분 가능
    • 오류 처리 로직을 분리하여 유지보수가 용이
  • 코드 가독성 향상
    • 일반적인 RuntimeException 대신 커스텀 예외를 사용하면 예외 발생 상황을 더 명확하게 전달
  • 예외 처리의 일관성 유지
    • 전역 예외 처리기에서 커스텀 예외를 처리하여 애플리케이션 전체에서 일관된 방식으로 오류 관리

커스텀 예외 클래스의 활용 장점

  • 코드 중복 제거
    • 예외 처리 로직을 한 곳에서 관리 가능
  • 비즈니스 로직과 예외 처리의 분리
    • 유지보수 용이
  • 가독성 향상
    • 예외 상황을 직관적으로 파악 가능
  • 일관된 예외 응답 제공
    • 클라이언트가 오류를 쉽게 이해하고 대응 가능

테스트 주도 개발

테스트(TEST) 개요

소프트웨어 개발에서 테스트는 프로그램의 기능, 동작, 특정 부분이 올바르게 작동하는지 확인하고 검증하는 과정
개발된 소프트웨어의 신뢰성을 보장하고 품질을 유지하는 중요한 단계
테스트는 기능적 요구사항과 비기능적 요구사항을 검증하여 사용자 요구를 충족

요구사항 검증

기능적 요구사항 검증

소프트웨어가 사용자가 요구한 기능을 정확히 수행하는지 검증

  • 요구사항 분석 → 테스트 케이스 설계 → 테스트 수행 → 검증 → 결함 보고 및 추적

비기능적 요구사항 검증

성능, 보안, 사용성, 신뢰성과 같은 품질 속성을 검증
사용자의 경험에 직접 영향을 미치며 소프트웨어의 성공에 중요한 요소

  • 주요 항목
    • 성능: 응답 시간, 처리량, 자원 사용률 평가
    • 보안: 취약점 분석 및 보안 위험 평가
    • 사용성: 사용자가 소프트웨어를 쉽게 이해하고 사용할 수 있는지 평가
    • 신뢰성: 소프트웨어가 장기간 안정적으로 작동하는지 평가

테스트 종류

  • 단위 테스트 (Unit Test)
    • 소프트웨어의 가장 작은 단위(모듈, 함수, 클래스 등)를 독립적으로 테스트
    • 개발자가 직접 작성하여 빠른 피드백을 제공하며 지속적인 테스트 가능
    • 개발 컴포넌트의 정확성을 보장하고 조기에 결함 발견
  • 통합 테스트 (Integration Test)
    • 단위 테스트를 통과한 모듈들을 통합하여 상호작용과 인터페이스의 정확성을 확인하는 테스트
    • 모듈 간의 데이터 전달 및 호환성을 검증
    • 결함을 조기에 발견하고 시스템 전체의 안정성을 향상
  • 시스템 테스트 (System Test)
    • 통합된 전체 시스템이 요구사항에 맞게 동작하는지 검증
    • 사용자 인터페이스, 외부 인터페이스, 데이터베이스 등 모든 구성 요소를 포함하여 테스트
    • 시스템 전체의 기능과 성능을 실환경에서 확인
  • 인수 테스트 (Acceptance Test)
    • 개발된 시스템이 사용자의 요구사항을 충족하는지 확인하는 테스트
    • 사용자 또는 고객의 관점에서 시스템의 기능과 품질 평가
    • 실제 사용 환경에서 원활하게 동작하는지 확인하여 최종 승인 결정

테스트 자동화

  • 반복적인 테스트 작업을 자동화하여 개발 및 배포 과정 가속화
  • 자동화된 테스트는 수동 테스트보다 빠르고 오류 가능성이 낮음
  • 웹 애플리케이션 테스트, 백엔드 서비스 검증, 인프라스트럭처 코드 검증 등 다양한 영역에서 활용
    • 대표적인 테스트 자동화 도구: Selenium, Jenkins, Travis CI, CircleCI 등

테스트 자동화 장점

  • 개발 사이클 단축
  • 소프트웨어 품질 유지
  • 테스트 커버리지 확대
  • 개발자의 작업 부담 감소

테스트 코드의 필요성

  1. 버그의 조기 발견 및 해결
    • 개발 과정 중 버그를 조기에 발견하고 수정 가능
    • 사용자에게 문제가 전달되기 전에 신속한 대응 가능
    • 비용 절감 및 프로젝트 일정 효율성 증가
  2. 코드의 동작 검증
    • 개발된 기능이 요구사항에 부합하는지 확인하는 필수적 과정
    • 함수나 모듈이 예상대로 정확히 작동하는지 검증하여 신뢰성 확보
  3. 리팩토링 및 유지보수 용이성
    • 코드 변경이 예상치 못한 영향 없이 안전하게 수행될 수 있도록 보호
    • 개발자가 코드 수정이나 기능 추가 시 안정적인 작업 환경 제공
  4. 개발자 간의 커뮤니케이션 향상
    • 코드 동작 방식에 대한 명확한 설명 역할 수행
    • 신규 팀원이 프로젝트 참여 시 코드의 의도와 기능을 빠르게 이해하는 데 도움

테스트 주도 개발 (TDD)

테스트 코드가 개발을 주도하는 방식
코드를 작성하기 전에 테스트 코드를 먼저 작성하는 개발 방법론

TDD의 기본 과정

tdd_06

  1. 테스트 코드 작성 (Red)
    • 구현할 기능에 대한 테스트 코드를 먼저 작성
    • 처음에는 테스트가 실패해야 함 (Red 상태)
  2. 실제 코드 작성 (Green)
    • 최소한의 코드로 테스트를 통과하도록 구현
    • 테스트가 성공하면 Green 상태
  3. 코드 리팩토링 (Refactor)
    • 리팩토링을 통해 코드의 효율성, 가독성, 유지보수성을 향상

TDD 장점

  • 높은 코드 품질
    • 테스트 커버리지를 높여 안정적인 코드 생산
  • 개발 초기부터 테스트 가능
    • 문제를 조기에 발견하고 수정 가능
  • 코드 유지보수성 향상
    • 코드 변경 시 발생할 수 있는 회귀 문제를 방지
  • 설계 개선
    • 코드의 모듈화를 촉진하여 아키텍처 개선
  • 코드 신뢰도 향상
    • 코드의 정확성을 보장하여 개발자가 자신감을 가지고 작업 가능

TDD 적용 시 고려사항

  • 단위 테스트 중심으로 적용
  • 명확하고 간결한 테스트 케이스 설계
  • 코드 품질 유지 (가독성 및 유지보수성 고려)
  • 테스트 자동화를 적극 활용
  • 개발 팀 전체의 협력과 공감대 형성 필수

테스트 코드 라이브러리

  • JUnit
    • 자바에서 가장 널리 사용되는 단위 테스트 프레임워크
    • TDD(Test-Driven Development)를 지원
  • TestNG
    • JUnit에서 영감을 받아 더 강력한 기능 제공
    • 주요 기능: 동시성 테스트, 파라미터화 테스트, 의존성 테스트
  • Mockito
    • 모킹(Mock) 프레임워크로 단위 테스트에서 의존성을 제어하기 위해 사용
    • 가짜 객체를 생성하여 독립적인 테스트 환경 구축
  • PowerMock
    • Mockito와 JUnit을 확장한 프레임워크
    • 일반적으로 모킹하기 어려운 final 클래스, static 메서드, private 메서드 등을 모의 가능
  • Spock
    • Groovy 기반의 테스트 프레임워크
    • BDD 스타일의 테스트, 파라미터화 테스트, 목 객체 생성 및 검증 지원

테스트(MockMvc)

MockMvc 개요

MockMvc는 스프링 프레임워크에서 제공하는 테스트 도구로 웹 애플리케이션의 컨트롤러 계층을 독립적으로 테스트할 수 있도록 지원
실제 HTTP 요청을 시뮬레이션하여 컨트롤러에 전달하고 응답을 검증하는 기능을 제공

MockMvc 주요 기능

MockMvc를 활용하면 컨트롤러가 클라이언트 요청을 정확하게 처리하고 응답하는지를 철저히 검증 가능

  1. HTTP 요청 시뮬레이션
    • GET, POST, PUT, DELETE 등의 요청을 모방하며 요청 파라미터, 헤더, 쿠키 등을 설정 가능
  2. 컨트롤러 실행
    • 시뮬레이션된 요청을 받아 컨트롤러의 메서드를 실행하며 이 과정에서 실제 의존성을 모의(Mock) 객체로 대체하여 독립적 테스트 수행
  3. 응답 검증
    • 실행된 컨트롤러의 결과를 캡처하여 기대치(응답 코드, 내용 등)와 비교하여 검증
  4. 의존성 주입
    • @MockBean 등의 어노테이션을 활용하여 컨트롤러에 필요한 의존성을 모의(Mock) 객체로 주입해 테스트 환경 구성

Mockito

Mockito 개요

Mock 객체를 쉽게 만들고 관리하고 검증할 수 있는 방법을 제공하는 Java 기반 프레임워크
Mock 객체를 활용하여 가짜 객체에 원하는 결과를 설정(Stub)하고 단위 테스트를 실행 가능
복잡한 의존성을 가진 객체의 테스트를 용이하게 하기 위해 사용

Mock 객체(Mock)

진짜 객체와 비슷하게 동작하지만 직접 행동을 관리하는 객체

  • 모킹(Mocking): 테스트를 위해 실제 객체 대신 모의(Mock) 객체를 생성하여 테스트의 독립성을 확보
  • 목업(Mock-up): 모의 객체를 메모리에서 생성하는 과정

Mock의 필요성

  • 테스트하려는 대상 객체가 메모리에 존재
  • 복잡한 객체를 테스트하기 위해 실제 객체와 유사한 가짜 객체를 생성하여 테스트를 단순화 가능
  • 의존성이 높은 객체 간의 결합을 줄여 독립적인 단위 테스트가 가능
  • @MockBean을 활용하여 특정 빈을 테스트에서 의존하지 않도록 가짜 빈(Mock Bean)으로 생성 가능

서블릿 컨테이너 모킹(Mocking)

웹 환경에서 컨트롤러 테스트 시 서블릿 컨테이너가 구동되며 DispatcherServlet 객체가 메모리에 올라가야 함
서블릿 컨테이너를 모킹하면 실제 서블릿 컨테이너 없이 모의(Mock) 환경에서 컨트롤러를 테스트 가능

Given-When-Then 패턴

BDD(Behavior-Driven Development, 행위 주도 개발)에서 사용되는 테스트 시나리오 작성 방식

  • Given(주어진 상황)
    • 테스트의 전체 조건이나 준비 상태를 기술하는 부분
    • 테스트를 실행하기 위해 필요한 데이터, 객체, 환경 등을 설정
  • When(조건)
    • 테스트 대상 시스템이나 컴포넌트에 대한 액션 또는 이벤트를 기술하는 부분
    • 사용자의 행동, 외부 시스템의 동작, 입력값 등을 표시시
  • Then(기대 결과)
    • 테스트 시나리오의 예상 결과와 검증 사항을 기술하는 부분
    • 시스템이 특정 동작 후 어떤 상태여야 하는지 어떤 결과를 반환해야 하는지 명시

Given-When-Then 패턴의 장점

  • 테스트 시나리오를 명확하고 일관된 형식으로 작성 가능
  • 테스트 코드의 가독성과 이해도를 높이며, 협업과 커뮤니케이션 원활화

테스트(Integration Test)

RestTemplate 개요

RestTemplate은 Spring에서 지원하는 내장 클래스로 REST 방식의 API를 간편하게 호출할 수 있도록 제공

  • Spring 3.0부터 지원되었으며 JSON, XML 등 다양한 형식의 응답을 처리 가능
  • HTTP 프로토콜의 주요 메서드(GET, POST, PUT, DELETE 등)를 지원

RestTemplate의 주요 특징

  • HTTP 메서드 지원
    • GET, POST, PUT, DELETE 등의 주요 HTTP 메서드를 지원
    • getForObject(), postForObject(), put(), delete() 등의 메서드를 통해 RESTful API의 CRUD 작업을 수행 가능
  • 다양한 응답 처리
    • 서버로부터 받은 응답을 JSON, XML 등 다양한 형식으로 처리
    • 응답 데이터를 자바 객체, String, Map, 커스텀 클래스 등의 형태로 자동 변환
  • 요청 파라미터 설정
    • 요청 시 다양한 파라미터를 설정 가능
    • URL 경로에 변수를 포함시키기 위해 @PathVariable을 사용 가능
    • 쿼리 파라미터를 추가하기 위해 @RequestParam을 사용 가능
    • 요청 헤더를 설정하여 인증 토큰, 콘텐츠 타입 등을 지정 가능
  • 예외 처리
    • API 호출 시 발생할 수 있는 예외를 처리하기 위한 기능을 제공
    • HttpClientErrorException, HttpServerErrorException 등의 예외를 통해 클라이언트 오류나 서버 오류를 감지 가능능
    • 예외 처리를 통해 적절한 대응을 하고 예외 메시지를 활용하여 디버깅 가능
  • 동기/비동기 호출
    • 기본적으로 동기 방식으로 API를 호출
    • 동기 방식은 요청을 보내고 응답을 받을 때까지 블로킹
    • 비동기 방식으로 API를 호출하고 싶다면 AsyncRestTemplate을 사용 가능
    • 비동기 호출을 사용하면 응답을 기다리지 않고 다른 작업을 수행할 수 있어 효율적

RestTemplate의 주요 과정

  1. RestTemplate 인스턴스 생성
    • RestTemplate 클래스의 인스턴스를 생성
  2. 요청 구성 설정
    • HTTP 메서드, 요청 헤더, 요청 본문, 요청 매개변수 등을 설정
    • 이는 요청을 보내기 전에 필요한 모든 정보를 준비하는 과정
  3. HTTP 요청 전송
    • 설정된 URI와 HTTP 메서드를 사용하여 요청 전송
    • HttpMessageConverter를 통해 Java 객체를 요청 본문에 맞는 메시지로 변환하여 전송
  4. 서버 처리 및 응답
    • 서버는 받은 요청을 처리하고 결과를 HTTP 응답으로 전송
  5. 응답 처리
    • RestTemplate은 응답을 받아 HTTP 상태 코드, 응답 헤더, 응답 본문 등을 분석
    • HttpMessageConverter를 이용해 응답 본문의 메시지를 Java 객체로 변환
  6. 결과 반환
    • 변환된 객체 또는 다른 필요한 정보를 애플리케이션에 반환

API 문서화(Swagger)

API 문서화 개요

클라이언트가 REST API를 사용할 때 필요한 요청 정보를 문서화한 것을 API 문서화
서버에서 만든 API를 클라이언트에서 사용하기 위해서는 해당 API의 규격과 사용 방법을 알아야 함

자동 API 문서화의 필요성

  • 효율성 향상
    • 개발자가 별도로 문서를 작성할 필요가 없어 시간과 노력을 절약 가능
  • 문서와 코드의 동기화 유지
    • 문서화 도구는 API 코드를 기반으로 문서를 자동 생성하므로 코드 변경 시 문서도 자동 업데이트
  • 표준화된 문서 구조 제공
    • API 문서의 일관성을 유지하여 개발자와 클라이언트가 쉽게 이해하고 사용 가능

Swagger UI

Swagger UI 개요

Swagger UI는 API 리소스를 시각적으로 표현하고 테스트할 수 있도록 도와주는 도구
Swagger는 OpenAPI 규격을 기반으로 문서를 자동 생성하며 백엔드와 클라이언트 모두 쉽게 활용 가능

Swagger UI의 주요 목적

  1. API 문서 자동 생성
    • API 코드의 주석을 분석하여 자동으로 문서를 생성
    • @ApiOperation, @ApiParam, @ApiResponse 등의 어노테이션을 사용하여 API 엔드포인트를 설명 가능
  2. 직관적인 사용자 인터페이스 제공
    • API의 계층 구조를 트리 형태로 표현하여 엔드포인트를 쉽게 탐색 가능
  3. API 테스트 기능 제공
    • API를 직접 실행해보면서 요청과 응답을 테스트 가능
  4. API 문서의 공유 및 협업 지원
    • Swagger UI 문서는 웹 페이지 형태로 제공되어 팀 내외부에서 쉽게 공유 가능

Swagger UI의 장점

  • 개발 효율성 향상: 문서 작성 시간을 절약하고 API 코드와 문서의 불일치를 방지
  • 커뮤니케이션 개선: 개발자 및 클라이언트가 API를 쉽게 이해하고 협업 가능
  • 테스트 및 디버깅 용이: API를 실시간으로 실행하고 테스트할 수 있는 환경 제공
  • 유지보수성 향상: API 코드 변경 시 자동으로 문서가 업데이트
This post is licensed under CC BY 4.0 by the author.