[Spring Boot] 섹션4 Clean Code

정수현·2025년 4월 14일

캡스톤

목록 보기
5/8

17강 Clean Code는 왜 중요한가

  • 코드 : 요구사항을 표현하는 언어
  • 개발자는 요구사항을 구현하기 위해 코드를 읽고 작성한다.
  • 유지 보수, 가독성을 위해 Clean Code는 중요하다.

우리가 작성한 Controller

  1. API 진입 지점으로써 HTTP Body 객체로 변환한다.
  2. 현재 유저가 있는지 없는지 확인하고 예외 처리를 한다.
  3. SQL을 사용해 실제 Database와의 통신을 담당한다.



18강 Controller 3단 분리하기

- Service와 Repository

Controller 함수 1개가 하고 있던 역할

  1. API 진입 지점으로써 HTTP Body 객체로 변환한다.
    Controller에 유지 (API, HTTP 담당)
  2. 현재 유저가 있는지 없는지 확인하고 예외 처리를 한다.
    Service의 역할 (분기 처리, 로직 담당)
  3. SQL을 사용해 실제 Database와의 통신을 담당한다.
    Repository의 역할 (DB와의 접근 담당)

위 세 개의 역할을 각각 나눌 것이다.

Layered Architecture

Repository → Service → Controller

jdbcTemplate.update() : 데이터를 바꾸는 쿼리 (INSERT, UPDATE, DELETE)
jdbcTemplate.query() : 데이터를 읽는 쿼리 (SELECT)



19강 UserController와 스프링 컨테이너

UserController의 의아한 점

  1. static 이 아닌 코드를 사용하려면 인스턴스화가 필요하다.
    → 어디서 UserController를 인스턴스화하고 있는 것인가 (new UserController();
  2. UserController는 JdbcTemplate이 필요하다.
    → UserController는 JdbcTemplate에 의존한다.
    ⇒ UserController는 JdbcTemplate이 없으면 동작하지 않는다!

UserController 자동 인스턴스화

  • @RestController
    ↪ API의 진입 지점을 생성함
    ↪ 💡 UserController 클래스를 스프링 빈으로 등록시킨다. 💡

스프링 빈

  • 서버가 시작되면, 스프링 서버 내부에 (스프링) 컨테이너가 생긴다.
  • 컨테이너 안에는 클래스가 들어가게 된다.
  • 다양한 정보(이름 - userController, 타입 - UserController)와 함께 인스턴스화(UserController userController = new UserController();)도 이루어진다.
  • UserController를 인스턴스화 하려면 JdbcTemplate이 필요하다.
  • 이 또한 스프링 빈으로 등록되어 있다.
  • Spring Initializr 에서 프로젝트 생성 전에 의존성(Dependence) 란에서 등록함
    ctrl + shift + Nbuild.gradledependencies에서 확인 가능

스프링 컨테이너

  • 서로 필요한 관계에 있는 스프링 빈끼리 연결해주는 역할
    ⇒ 필요한 클래스끼리 연결해준다.

정리

  1. 서버가 시작되면
  2. 스프링 컨테이너가 생성된다.
  3. 스프링 컨테이너 안에는 많은 스프링 빈(클래스)들이 등록된다.
    ex) JdbcTemplate, DataSource, Environment 등 ...
    UserController 우리가 설정해준 스프링 빈도 등록된다.
  4. 이때 필요한 의존성이 자동으로 설정된다.

UserRepositoryJdbcTemplate을 가져오지 못하는 이유

💡 JdbcTemplate을 가져오려면 UserRepository가 스프링 빈이어야 하는데 UserRepository는 스프링이 아니다.

UserRepository를 스프링 빈으로 등록하면 JdbcTemplate을 가져올 수 있다.

코드 작성 예시

수정 전

UserController

@RestController
public class UserController {
    private final UserService userService = new UserService();
    
	public UserController(JdbcTemplate jdbcTemplate)
	{
		this.userService = new UserService(jdbcTemplate);
	}
}

UserSerivce

public class UserService {
    private final UserRepository userRepository;

    public UserService(JdbcTemplate jdbcTemplate)
    {
        userRepository = new UserRepository(jdbcTemplate);
    }
}

UserRepository

public class UserRepository {
    private final JdbcTemplate jdbcTemplate;

    public UserRepository(JdbcTemplate jdbcTemplate)
    {
        this.jdbcTemplate = jdbcTemplate;
    }
}

수정 후

UserController

@RestController
public class UserController {
    private final UserService userService;
    
	public UserController(UserService userService)
	{
		this.userService = userService;
	}
}

UserSerivce

@Service
public class UserService {
    private final UserRepository userRepository;

    public UserService(UserRepository userRepository)
    {
        this.userRepository = userRepository;
    }
}

UserRepository

@Repository
public class UserRepository {
    private final JdbcTemplate jdbcTemplate;

    public UserRepository(JdbcTemplate jdbcTemplate)
    {
        this.jdbcTemplate = jdbcTemplate;
    }
}
  • UserRepositoryJdbcTemplate을 가져오고
  • UserServiceUserRepository를 가져오고
  • UserControllerUserService를 가져온다.



20강 스프링 컨테이너를 왜 사용할까

BookControllerBookServiceBookRepositoryBookMemoryRepository or BookMySqlRepository

@Repository
@Service
@RestController
  1. 위와 같은 어노테이션을 통해 스프링 컨테이너를 생성한다.
  2. 스프링 컨테이너를 통해 스프링 빈이 만들어진다. (필요한 클래스들끼리 연결)
  3. 스프링 컨테이너를 생성하면 자동으로 인스턴스화 해주기 때문에 private final BookRepository bookRepository = new BookRepository(); 이런 과정을 거치지 않아도 된다.
  4. 또한 스프링 컨테이너를 사용하면 BookService에서 BookMemoryRepository를 사용할지, BookMySqlRepository를 사용할지 선택한다.

위와 같은 방식을 제어의 역전이라고 한다.

제어의 역전

💡 스프링 컨테이너가 어떤 Repository를 사용할지 대신 결정해주는 방식을 제어의 역전(IoC, Inversion of Control)이라고 한다.


💡 컨테이너가 Repository를 선택해 BookService에 넣어주는 과정을 의존성 주입 (DI, Dependency Injection)이라고 한다.

의존성 주입

@Primary

  • 어떤 Repository를 사용할지 결정하도록 Repository에 우선권을 부여한다.
@Primary
@Repository
public class BookMemoryRepository implements BookRepository
{

}



21강 스프링 컨테이너를 다루는 방법

스프링 빈을 등록하는 방법

@Configuration

  • 클래스에 붙이는 어노테이션
  • @Bean을 사용할 때 함께 사용한다.

@Bean

  • 메소드에 붙이는 어노테이션
  • 메소드에서 반환되는 객체를 스프링 빈에 등록한다.
@Configuration
public class UserConfiguration {
    
    @Bean
    public UserRepository userRepository(JdbcTemplate jdbcTemplate)
    {
        return new UserRepository(jdbcTemplate);    
    }    
}

@Service, @Repository

  • 개발자가 직접 만든 클래스를 스프링 빈으로 등록할 때 사용한다.

@Configuration + @Bean

  • 외부 라이브러리, 프레임워크에서 만든 클래스를 등록할 때

@Component

  • 주어진 클래스를 '컴포넌트'로 간주한다.
  • 이 클래스들은 스프링 서버가 뜰 때 자동으로 감지된다.
  • 우리가 사용하는 어노테이션들을 자동 감지한다.
  • 스프링 빈 등록 방법 중 하나이다.
  1. Controller, Service, Repository가 모두 아니고
  2. 개발자가 직접 작성한 클래스를 스프링 빈으로 등록할 때 사용되기도 한다.

스프링 빈 등록 방법

  1. (가장 권장) 생성자를 이용해 주입 받는다.
    @Autowired 생략 가능

  2. setter & @Autowired 사용
    → 오작동의 원인이 될 수 있음

public class UserController
{
	private UserService userService;

    @Autowired
    public void setUserService(UserService userService) 
    {
        this.userService = userService;
    }
}
  1. 필드에 직접 @Autowired를 적는다.
    → 테스트를 어렵게 만드는 요인
@Autowired
private JdbcTemplate jdbcTemplate;

@Qualifier

  • 여러개의 후보 클래스들이 있을 때, 그 중 하나를 특정해서 가져오고 싶은 경우에 사용한다.
  • 스프링 빈을 사용하는 쪽, 등록하는 쪽 모두 @Qualifier 를 사용할 수 있다.
  • 방법 1 : 스프링 빈을 사용하는 쪽에서만 쓰면, 빈의 이름을 적어주어야 한다.
  • 방법 2 : 양쪽 모두 사용하면, @Qualifier 끼리 연결된다.

스프링 빈 등록 예시 1

UserControllerFruitServiceAppleService | BananaService | OrnageService

private final FruitService fruitService;

public UserController(@Qualifier("appleService") FruitService fruitService)
{
	this.fruitService = fruitService;
}

스프링 빈 등록 예시 2

@RestController
public class UserController {

    private final FruitService fruitService;

    public UserController(UserService userService, @Qualifier("main") FruitService fruitService)
    {
        this.fruitService = fruitService;
    }
}

-------------------------------------------------------

@Service
@Qualifier("main")
public class BananaService implements FruitService
{

}

@Primary vs @Qualifier

  • 사용자가 직접 적어준@Qualifier가 더 우선이다.

정리 : 배운 내용

  1. 좋은 코드가 왜 중요한지 이해하고, 원래 있떤 Controller 코드를 보다 좋은 코드로 리팩토링한다.
  2. 스프링 컨테이너와 스프링 빈이 무엇인지 이해한다.
  3. 스프링 컨테이너가 왜 필요한지, 좋은 코드와 어떻게 연관이 있는지 이해한다.

0개의 댓글