제가 공부한 내용을 정리하는 블로그입니다.
아직 많이 부족하고 배울게 너무나도 많습니다. 틀린내용이 있으면 언제나 가감없이 말씀해주시면 감사하겠습니다😁

스프링 정리를 위한 포스팅입니다.
해당 포스팅은 @Controller, @Service, @Repository의 역할과 특징 정리입니다.

 

도입

Spring 프레임워크는 계층구조로 코드를 나누어 각 계층마다 역할을 분리하였습니다.(객체 지향적 특징)
이와 같은 계층 구조는 코드의 역할 분리를 명확히 하고 유지보수성을 높이는 데 중요한 역할을 합니다.

  • Presentation Layer: 클라이언트의 요청 처리
  • Business Layer: 비즈니스 로직 처리
  • Persistence Layer: 데이터 접근 처리

클라이언트와의 요청 처리는 @Controller, 비즈니스 로직 처리는 @Service, 데이터베이스와의 통신은 @Repository를 통해 코드를 구성합니다. 

 

이번 포스팅에서는 @Controller, @Service, @Repository의 역할과 특징을 살펴보겠습니다.

 

@Controller

@Controller는 Spring MVC에서 컨트롤러 클래스를 정의할 때 사용되는 어노테이션입니다. 일반적으로 뷰 템플릿을 반환하는 방식으로 클라이언트의 요청을 처리합니다. 이 어노테이션이 있는 클래스는 HTTP 요청을 처리하고, Model 객체를 사용하여 데이터를 뷰에 전달합니다. @Controller는 주로 웹 애플리케이션에서 서버 사이드 렌더링을 위한 컨트롤러로 사용됩니다.

@Controller 구조

  1. 클라이언트로부터 받은 요청을 통해 Handler 어댑터를 통해 해당 요청을 처리할 수 있는 controller를 찾습니다.
  2. controller는 service와 repository의 비즈니스 로직을 처리한 후 뷰 이름을 handlerAdapter에게 전달합니다.
  3. Dispatcher Servlet은 해당 뷰 네임에 해당하는 뷰를 ViewResolver를 통해서 뷰를 반환합니다.
@Controller
@RequiredArgsConstructor
public class UserController {
    private final UserService userService;

    @GetMapping("/users")
    public String getUsers(Model model) {
        model.addAttribute("users", userService.findAll());
        return "userList"; // "userList" 뷰 반환
    }
}

userList.html

 

@Controller + @RequestBody

@RequestBody는 HTTP 요청 본문을 자바 객체로 변환하여 매핑해주는 어노테이션입니다. 이 어노테이션을 @Controller와 함께 사용하면, 클라이언트에서 전달한 JSON, XML 등의 데이터를 객체로 받을 수 있습니다. @Controller는 뷰를 반환하는 역할을 계속하고, @RequestBody는 데이터를 뷰에 매핑하는 역할을 합니다.

@Controller + @RequestBody 구조

  1. 클라이언트로부터 받은 요청을 통해 Handler 어댑터를 통해 해당 요청을 처리할 수 있는 controller를 찾습니다.
  2. controller는 service와 repository의 비즈니스 로직을 처리한 후 뷰 이름을 handlerAdapter에게 전달합니다.
  3. 반환되는 객체는 Json으로 Serialize되어 사용자에게 반환됩니다. 이는 @ResponseBody 어노테이션으로 개발됩니다.
@Controller
@RequiredArgsConstructor
public class UserController {
    private final UserService userService;

    /**
     * Handles POST requests to create a new user from JSON data and renders the "userDetail" view.
     *
     * @param user the user data from the request body
     * @param model the model to hold attributes for the view
     * @return the name of the view to be rendered
     */
    @PostMapping("/users")
    public String createUser(@RequestBody User user, Model model) {
        User savedUser = userService.save(user);
        model.addAttribute("user", savedUser);
        return "userDetail"; // Return the name of the view
    }
}

성공적인 response

 

@RestController

@RestController는 @Controller와 @ResponseBody를 결합한 어노테이션입니다. @RestController가 적용된 클래스는 RESTful 웹 서비스를 제공하며, 메서드의 반환 값이 바로 HTTP 응답 본문으로 전송됩니다. 따라서 @RestController는 일반적으로 JSON, XML과 같은 데이터를 직접 반환하는 데 사용됩니다. 뷰를 반환하지 않으며, 주로 API 서버에서 사용됩니다.

@RestController

  1. 클라이언트로부터 받은 요청을 통해 Handler 어댑터를 통해 해당 요청을 처리할 수 있는 controller를 찾습니다.
  2. controller는 service와 repository의 비즈니스 로직을 처리한 후 뷰 이름을 handlerAdapter에게 전달합니다.
  3. 반환되는 객체는 Json으로 Serialize되어 사용자에게 반환됩니다. 이는 @RestController 어노테이션으로 개발됩니다.
@RestController
@RequiredArgsConstructor
public class UserController {
    private final UserService userService;

    /**
     * Handles GET requests to retrieve the list of users as JSON.
     *
     * @return a list of users
     */
    @GetMapping("/json-users")
    public List<User> getUsers() {
        return userService.findAll();
    }
}

 

@Service

@Service 어노테이션은 서비스 계층에 사용됩니다. 서비스 계층은 비즈니스 로직을 처리하는 곳으로, 일반적으로 데이터베이스에서 데이터를 조회하고, 가공하여 컨트롤러로 전달하는 역할을 합니다. @Service는 주로 서비스 클래스를 나타내며, @Component의 특성을 그대로 가지고 있어 Spring 컨테이너에 의해 관리됩니다.

@Service
@RequiredArgsConstructor
public class UserService {
    private final UserRepository userRepository;

    public List<User> findAll() {
        return userRepository.findAll();
    }
}

 

 

@Repository

@Repository는 데이터 접근 계층에 사용됩니다. 이 어노테이션은 주로 데이터베이스와 상호작용하는 클래스를 나타냅니다. @Repository 어노테이션을 사용하면 Spring이 해당 클래스를 자동으로 데이터 접근 객체(DAO)로 인식하고, 예외 변환 기능을 제공합니다. @Repository는 @Service와 동일하게 @Component의 특성을 그대로 가지고 있어 Spring 컨테이너에 의해 관리되며, 데이터베이스와의 상호작용을 담당합니다.

// In-memory Database
@Repository
public class ItemRepository {

    private static final Map<Long, Item> store = new HashMap<>(); //static
    private static long sequence = 0L; //static

    public Item save(Item item) {
        item.setId(++sequence);
        store.put(item.getId(), item);
        return item;
    }
}


// JPA
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    List<User> findAll();
}

 

 

코드

제가 공부한 내용을 정리하는 블로그입니다.
아직 많이 부족하고 배울게 너무나도 많습니다. 틀린내용이 있으면 언제나 가감없이 말씀해주시면 감사하겠습니다😁

스프링 정리를 위한 포스팅입니다.
해당 포스팅은 Lombok 어노테이션 입니다.

 

Lombok이란?

Lombok은 Java 애플리케이션에서 보일러플레이트 코드(반복적인 코드)를 줄여주는 라이브러리입니다.

이를 통해 코드의 가독성을 높이고 유지보수를 쉽게 만들어 줍니다.

왜 Lombok을 사용해야 하는가?

  • Java의 기본 클래스는 자주 사용되는 메서드(생성자, getter, setter, toString 등)수동으로 작성해야 합니다.
  • Lombok은 이러한 반복적인 코드 생성을 자동화하여 개발 생산성을 크게 향상시킵니다.

Lombok 어노테이션 종류

@Getter / @Setter

  • 클래스의 모든 필드에 대해 getter와 setter 메서드를 자동으로 생성합니다.
@Getter @Setter
public class Person {
    private String name;
    private int age;
}

@ToString

  • 객체의 필드를 문자열로 변환하는 toString() 메서드를 자동으로 생성합니다.
@ToString 
public class Person { 
    private String name; 
    private int age; 
}

@NoArgsConstructor / @AllArgsConstructor

  • 기본 생성자 및 모든 필드를 초기화하는 생성자를 자동으로 생성합니다.
@NoArgsConstructor
@AllArgsConstructor
public class Animal {
    int age;
    String name;
}

@Builder

  • 빌더 패턴을 적용하여 객체 생성 시 가독성을 높입니다.
@Builder
public class Animal {
    int age;
    String name;
}

 

@Data

  • @Getter, @Setter, @ToString, @EqualsAndHashCode, @RequiredArgsConstructor를 모두 포함하는 어노테이션입니다. 주로 DTO 객체에 사용됩니다.
@Data
public class Plant {
    private int height;
    private String origin;

    public Plant(int height, String origin) {
        this.height = height;
        this.origin = origin;
    }
}

Lombok 설치방법

spring에서 lombok을 설치하기 위해서는 maven 또는 gradle을 사용하면 됩니다. 저는 gradle을 사용하겠습니다. 

build.gradle에서 compileOnly 'org.projectlombok:lombok'annotationProcessor 'org.projectlombok:lombok'

를 설정한 후에 gradle 프로젝트를 reload 하면 됩니다.

dependencies {
	// ...
	compileOnly 'org.projectlombok:lombok'
	annotationProcessor 'org.projectlombok:lombok'
}

예제 확인

public class Test {
    public static void main(String[] args) {
        // @Getter, @Setter
        Person person = new Person(1, 3, 4, 5, 8);
        System.out.println(person.getShoes()); // getShoes() automatically generated
        person.setShoes(84); // setShoes() automatically generated
        System.out.println(person.getShoes());

        // @ToString
        System.out.println("person.toString: " + person.toString()); // toString() automatically generated

        // @NoArgsConstructor / @AllArgsConstructor
        Animal tomy = new Animal(20, "tomy");
        Animal aymee = new Animal(); // Default constructor automatically generated
        System.out.println("tomy = " + tomy); // toString() automatically generated
        System.out.println("aymee = " + aymee); // toString() automatically generated

        // @Builder
        Animal hiro = Animal.builder().age(7).name("hiro").build(); // Using builder pattern
        System.out.println("hiro.toString() = " + hiro); // toString() automatically generated

        // @EqualsAndHashCode, @RequiredArgsConstructor
        Plant plant1 = new Plant(2, "korea");
        Plant plant2 = new Plant(2, "korea");

        // @Data
        System.out.println("plant1 equals plant2: " + plant1.equals(plant2)); // true, since fields are the same
        System.out.println("plant1 hashCode: " + plant1.hashCode()); // Same hashCode for equal objects
        System.out.println("plant2 hashCode: " + plant2.hashCode()); // Same hashCode for equal objects

        // @RequiredArgsConstructor automatically generates a constructor with final fields
        Plant plant3 = new Plant(3, "japan"); // Constructor with height and origin fields
        System.out.println("plant3: " + plant3); // toString() automatically generated
    }
}

결과 확인

다음과 같이 모든 어노테이션에 대해 결과를 확인할 수 있습니다.

 

코드

+ Recent posts