[TIL] 스프링 입문 TIL - 26/04/10

신여원·2026년 4월 10일

내일배움캠프

목록 보기
5/15

오늘 할 일

공부한 내용

DTO

DTO (Data Transfer Object)
: 데이터 전송 및 이동을 위해 생성되는 객체

  • Client에서 보내오는 데이터를 객체로 처리할 때 사용됨
  • 서버의 계층간의 이동에 사용된
  • DB와의 소통을 담당하는 Java 클래스를 그대로 Client에 반환하는 것이 아니라 DTO로 한번 변환한 후 반환할 때도 사용됨
  • Request의 데이터를 처리할 때 사용되는 객체는 RequestDto, Response를 할 때 사용되는 객체는 ResponseDto라는 이름을 붙여 DTO 클래스를 만들 수 있음
    - 절대적인 규칙은 아님

Database

Database
: 데이터의 집합

  • DBMS (Database Management System): Database를 관리하고 운영하는 소프트웨어
  • RDBMS (Relational DBMS): 관계형 데이터베이스
    ex) Oracle, MySQL
    - table이라는 최소 단위로 구성, 각 table은 열(column)과 행(row)으로 이루어져있음

SQL

JOIN 이해하기

  • 나누어진 테이블을 하나로 합치기 위해 데이터베이스가 제공하는 기능
  • ON이라는 키워드를 통해 기준이 되는 컬럼을 선택하여 2개의 테이블을 합쳐줌
  • JOIN 할 때는 적어도 하나의 컬럼을 서로 공유하고 있어야 하기 때문에, 테이블에 외래 키가 설정되어있다면 해당 컬럼을 통해 JOIN을 하면 조건을 충족할 수 있음
    다만, JOIN을 하기 위해 외래 키를 설정하는 것이 항상 좋은 선택이 아닐 수 있음
    - 외래 키를 설정하면 데이터 무결성을 확인하는 추가 연산이 발생
    - 무결성을 지켜야하기 때문에, 상황에 따라 개발하는데 불편할 수 있음
    => 상황에 따라 가장 효율적인 제약조건 테이블에 적용하기

JDBC

  • 실제로 서버가 데이터베이스와 소통하는 방법

    JDBC (Java Database Connectivity)
    : DB에 접근할 수 있도록 Java에서 제공하는 API

  • JDBC에 연결해야하는 DB의 JDBC 드라이버를 제공하면 DB 연결 로직을 변경할 필요없이 DB 변경이 가능함
    - JDBC 드라이버: DB 회사들이 자신들의 DB에 맞도록 JDBC 인터페이스를 구현한 후 제공하는 라이브러리
    -> ex) MySQL 드라이버를 사용해 DB에 연결을 하다 PostgreSQL 서버로 변경이 필요할 때 드라이버만 교체하면 손쉽게 DB 변경이 가능함

    JdbcTemplate
    : 커넥션 연결, statement 준비 및 실행, 커넥션 종료 등의 반복적이고 중복되는 작업들을 대신 처리해줌
    <- JDBC의 등장으로 손쉽게 DB교체가 가능해졌지만 DB에 연결하기 위해 여러가지 작업 로직을 직접 작성해야한다는 불편한 남았기 때문

=> JdbcTemplate이 JDBC를 직접 사용할 때 발생하는 불편함을 해결해주었지만, 여전히 복잡하고 사용하기 까다롭기 때문에 Java 개발자들을 위해 DB와 객체를 매핑하여 소통할 수 있는 ORM이라는 기술이 등장함

Layer Architecture

  • 이전 메모장 프로젝트의 문제점: Controller 클래스 하나로 모든 API를 처리하고 있다.
    - 당장은 API 수가 적고 기능이 단순해 코드가 복잡해 보이지 않지만, 앞으로 기능이 추가되고 복잡해진다면 문제가 발생할 수 있다.

    Spring의 3 Layer Architecture
    - 서버에서의 처리 과정이 대부분 비슷하다는 점을 이용해, 처리 과정을 크게 Controller, Service, Repository 3개로 분리함

Controller
: 클라이언트의 요청을 받음, Service에서 처리 완료된 결과를 클라이언트에게 응답함

  • 요청에 대한 로직 처리는 Service에게 전담
    - Request 데이터가 있다면 Service에 같이 전달함

Service
: 사용자의 요구사항을 처리('비즈니스 로직')하는 실세

  • DB 저장 및 조회가 필요할 때는 Repository에게 요청함

Repository
: DB 관리(연결, 해제, 자원 관리)

  • DB CRUD 작업 처리

IoC(제어의 역전), DI(의존성 주입)

  • 객체지향의 SOLID 원칙 그리고 GoF의 디자인 패턴과 같은 설계 원칙 및 디자인 패턴
    ex) 김치 볶음밥을 맛있게 만드는 방법 - 설계 원칙 / 김치 볶음밥 레시피 - 디자인 패턴
  • 좋은 코드를 위한 Spring의 IoC와 DI
    • 좋은 코드란: 논리가 간단, 중복 제거하고 표현 명확, 코드를 처음 보는 사람도 쉽게 이해하고 수정할 수 있어야함, 의존성 최소화, 새 기능 추가하더라도 크게 구조 변경 없어야함, ...
    • IoC와 DI는 좋은 코드 작성을 위한 Spring의 핵심 기술 중 하나임
    • DI 패턴을 사용하여 IoC 설계 원칙을 구현하고 있다.

의존성

  • DI를 이해하기 위해 의존성이해 필요
  • 강한 결합

    public class Consumer {
    
       void eat() {
           Chicken chicken = new Chicken();
           chicken.eat();
       }
    
       public static void main(String[] args) {
           Consumer consumer = new Consumer();
           consumer.eat();
       }
    }
    
    class Chicken {
       public void eat() {
           System.out.println("치킨을 먹는다.");
       }
    }
    	```
    강하게 결합되어있는 ConsumerChicken
    	- Consumer이 치킨이 아니라 피자를 먹고 싶어 한다면, 많은 수의 코드 변경이 불가피함
     
    	```
     public class Consumer {
    
         void eat(Food food) {
             food.eat();
         }
    
         public static void main(String[] args) {
             Consumer consumer = new Consumer();
             consumer.eat(new Chicken());
             consumer.eat(new Pizza());
         }
     }
    
     interface Food {
         void eat();
     }
    
     class Chicken implements Food{
         @Override
         public void eat() {
             System.out.println("치킨을 먹는다.");
         }
     }
    
     class Pizza implements Food{
         @Override
         public void eat() {
             System.out.println("피자를 먹는다.");
         }
     }
    	```
     JavaInterface를 활용해 해결
     	- Interface 다형성의 원리를 사용하여 구현하면 고객이 어떠한 음식을 요구하더라도 쉽게 대처할 수 있음
     

주입
: 여러 방법을 통해 필요로 하는 객체를 해당 객체에 전달하는 것

  • 필드에 직접 주입
public class Consumer {

    Food food;

    void eat() {
        this.food.eat();
    }

    public static void main(String[] args) {
        Consumer consumer = new Consumer();
        consumer.food = new Chicken();
        consumer.eat();

        consumer.food = new Pizza();
        consumer.eat();
    }
}

interface Food {
    void eat();
}

class Chicken implements Food{
    @Override
    public void eat() {
        System.out.println("치킨을 먹는다.");
    }
}

class Pizza implements Food{
    @Override
    public void eat() {
        System.out.println("피자를 먹는다.");
    }
}
Food를 Consumer에 포함시키고, Food에 필요한 객체를 주입 받아 사용할 수 있음
  • 메서드를 통한 주입
public class Consumer {

    Food food;

    void eat() {
        this.food.eat();
    }

    public void setFood(Food food) {
        this.food = food;
    }

    public static void main(String[] args) {
        Consumer consumer = new Consumer();
        consumer.setFood(new Chicken());
        consumer.eat();

        consumer.setFood(new Pizza());
        consumer.eat();
    }
}

interface Food {
    void eat();
}

class Chicken implements Food{
    @Override
    public void eat() {
        System.out.println("치킨을 먹는다.");
    }
}

class Pizza implements Food{
    @Override
    public void eat() {
        System.out.println("피자를 먹는다.");
    }
}
set 메서드를 사용하여 필요한 객체를 주입받아 사용할 수 있음
  • 생성자를 통한 주입
public class Consumer {

    Food food;

    public Consumer(Food food) {
        this.food = food;
    }

    void eat() {
        this.food.eat();
    }

    public static void main(String[] args) {
        Consumer consumer = new Consumer(new Chicken());
        consumer.eat();

        consumer = new Consumer(new Pizza());
        consumer.eat();
    }
}

interface Food {
    void eat();
}

class Chicken implements Food{
    @Override
    public void eat() {
        System.out.println("치킨을 먹는다.");
    }
}

class Pizza implements Food{
    @Override
    public void eat() {
        System.out.println("피자를 먹는다.");
    }
}
생성자를 사용하여 필요한 객체를 주입받아 사용할 수 있음

제어의 역전

  • 이전에는 Consumer가 직접 Food를 만들어 먹었기 때문에 새로운 Food를 만들려면 추가적인 요리준비(코드변경)가 불가피했음
    -> 이때는 제어의 흐름이 Consumer -> Food 였음
  • 이를 해결하기 위해 만들어진 Food를 Consumer에게 전달해주는 식으로 변경함으로써 Consumer는 추가적인 요리 준비(코드변경) 없이 어느 Food가 되었든지 전부 먹을 수 있게됨
    -> 결과적으로 제어의 흐름이 Food -> Consumer로 역전됨
    (실제 세계에서도, 고객이 음식을 만드는 것이 아니라, 만들어진 음식이 고객에게 전달되는 것이기 때문)

강한 결합 상태의 메모장 프로젝트

  • 강한 결합이 존재하는 메모장 프로젝트
    1. Controller가 Service 객체를 생성하여 사용

        public class Controller1 {
            private final Service1 service1;
    
            public Controller1() {
                this.service1 = new Service1();
            }
        }

    .
    2. Service가 Repository 객체를 생성하여 사용
    3. Repository 객체 선언

  • 강한 결합의 문제점:

    • 5개의 Controller가 각각 Service1을 생성하여 사용함
    • Repository1 생성자 변경에 의해 => 모든 Controller와 모든 Service의 코드 변경이 필요함
  • MemoService에서 자신은 사용하지 않지만 MemoRepository를 사용하기 위해 MemoRepository의 생성자에 JdbcTemplate을 넣어주고 있음 (MemoController에서도 마찬가지)

  • 강한 결합 해결 방법

    1. 각 객체에 대한 객체 생성은 딱 1번만
    2. 생성된 객체를 모든 곳에서 재사용
    3. 생성자 주입을 사용하여 필요로하는 객체에 해당 객체 주입.
    • Repository1 클래스 선언 및 객체 생성 -> repository1

      public class Repository1 { ... }
      
      // 객체 생성
      Repository1 repository1 = new Repository1();
    • Service1 클래스 선언 및 객체 생성 (repository1 사용) -> service1

      Class Service1 {
          private final Repository1 repitory1;
      
          // repository1 객체 사용
          public Service1(Repository1 repository1) {
              this.repository1 = new Repository1();
              this.repository1 = repository1;
          }
      }
      
      // 객체 생성
      Service1 service1 = new Service1(repository1);
    • Controller1 선언 (service1 사용)

      Class Controller1 {
          private final Service1 service1;
      
          // service1 객체 사용
          public Controller1(Service1 service1) {
              this.service1 = new Service1();
              this.service1 = service1;
          }
      }

      => 개선 결과:
      - Repository1, Service1 생성자 변경은 다른 곳에 영향을 주지 않음
      => 느슨한 결합

  • 제어의 역전 (IoC: Inversion of Control)

    • 강한 결합 상태의 메모장 프로젝트는 비효율적
      (제어의 흐름: Controller -> Service -> Repository)
    • 하지만, DI(의존성 주입)를 통해 제어의 흐름을 Repository -> Service -> Controller 로 역전함으로써 효율적 코드로 바꿈

문제 & 오류

내일 할 일

profile
경북대학교 컴퓨터학부

0개의 댓글