[Spring] 어노테이션 이해하기
KUKJIN LEE • 2개월 전 작성
다양한 어노테이션을 통해 코드를 효율적으로 작성하고, 개발자들이 더 직관적으로 애플리케이션을 설계할 수 있도록 돕습니다.
Spring에서 어노테이션은 특정 역할과 책임을 명시적으로 표현하며, 코드의 가독성과 유지보수성을 높입니다. 어노테이션을 통해 빈 관리, 트랜잭션 처리, 웹 요청 처리 등의 복잡한 작업을 간현하게 처리할 수 있습니다.
자주 사용되는 주요 어노테이션
-
@Component: 기본적인 스프링 빈을 정의하는 어노테이션입니다. 스프링이 해당 클래스를 빈으로 관리하게 됩니다.
-
@Service: 비즈니스 로직을 처리하는 클래스에 사용됩니다. 서비스 계층에서 주로 활용됩니다.
-
@Controller: 웹 요청을 처리하는 컨트롤러 클래스에 사용됩니다. 클라이언트의 요청을 받아서 적절한 응답을 반환합니다.
-
@Repository: 데이터 접근 계층(DAO)에서 사용되며, 주로 데이터베이스와의 상호작용을 담당하는 클래스에 사용됩니다.
-
@RestController: RESTful API를 처리하는 컨트롤러에 사용됩니다. 자동으로 응답 데이터를 JSON 또는 XML 형식으로 변환합니다.
-
@Autowired: 의존성 주입을 자동으로 처리해주는 어노테이션입니다. 주로 생성자, 필드, 메서드에 사용되어 필요한 빈을 주입합니다.
-
@Transactional: 트랜잭션 관리를 위한 어노테이션으로, 트랜잭션 범위 내에서 데이터베이스 작업이 처리되도록 보장합니다.
-
@Configuration: 설정 파일을 정의하는 데 사용됩니다. 스프링 컨테이너에 하나 이상의 빈을 설정하고 등록하는 클래스를 나타냅니다.
-
@Bean: 빈을 정의할 때 사용되며, 주로
@Configuration
클래스 내에서 특정 객체를 빈으로 등록할 때 활용됩니다.
어노테이션을 효과적으로 사용하는 법
1) @Service vs @Component: 언제 사용해야 할까?
-
@Component는 스프링 빈을 정의하는 가장 기본적인 어노테이션입니다. 그럼에도 불구하고 @Service를 사용하는 이유는 의도를 명확하게 하기 위함입니다. @Service를 붙이면 해당 클래스가 비즈니스 로직을 처리하는 서비스 계층임을 명확히 알 수 있습니다.
2) @Controller vs @RestController: 차이점은 무엇인가?
-
@Controller는 웹 요청을 처리하지만, 별도로 @ResponseBody를 사용해야 응답을 반환할 수 있습니다.
-
@RestController는 @Controller와 @ResponseBody를 결합한 형태로, 모든 메서드가 자동으로 JSON 또는 XML 데이터를 반환하므로, REST API를 작성할 때 유용합니다.
3) @Autowired: 생성자 주입을 활용한 안전한 의존성 주입
-
@Autowired는 주로 생성자 주입을 통해 사용하는 것이 권장됩니다. 생성자 주입은 불변성을 보장하고, 테스트 작성 시 모킹(Mocking)이 더 쉬워집니다.
@Service
public class UserService {
private final UserRepository userRepository;
@Autowired
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
// 비즈니스 로직
}
4) @Transactional: 트랜잭션 관리로 데이터 안전성 확보
-
데이터베이스 작업을 수행할 때 @Transactional을 사용하여 트랜잭션 처리를 보장할 수 있습니다. 트랜잭션 안에서 모든 작업이 성공해야만 커밋되며, 하나라도 실패하면 롤백됩니다.
@Service
public class UserService {
@Transactional
public User createUser(String name, String email) {
User user = new User(name, email);
return userRepository.save(user);
}
}
3. 실습 예시: 사용자 생성 기능
1) 사용자 엔티티 클래스 (User.java
)
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.*;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String email;
}
-
@Data: Lombok 어노테이션으로 getter, setter, toString, equals, hashCode를 자동 생성합니다.
-
@AllArgsConstructor: 모든 필드를 포함하는 생성자를 자동 생성합니다.
-
@NoArgsConstructor: 기본 생성자를 자동 생성합니다.
-
@Entity: 이 클래스가 JPA 엔티티임을 나타내며, 데이터베이스 테이블에 매핑됩니다.
2) 사용자 리포지토리 인터페이스 (UserRepository.java
)
기본적인 CRUD 기능(예: save()
, findById()
, findAll()
)을 제공하는 것뿐만 아니라, 데이터 접근을 구조화하고 유지보수성을 높이기 위해 반드시 필요합니다.
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
User findByEmail(String email); // 이메일로 사용자 조회
}
3) 사용자 서비스 클래스 (UserService.java
)
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class UserService {
private final UserRepository userRepository;
@Autowired
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Transactional
public User createUser(String name, String email) {
User user = new User(null, name, email); // 사용자 객체 생성 (ID는 null로 설정)
return userRepository.save(user); // UserRepository를 통해 저장
}
public User getUserByEmail(String email) {
return userRepository.findByEmail(email); // 이메일을 통해 사용자 조회
}
}
-
createUser(): 사용자를 생성하고 데이터베이스에 저장합니다.
-
getUserByEmail(): 이메일로 사용자를 조회합니다.
4) 사용자 컨트롤러 클래스 (UserController.java
)
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/users")
public class UserController {
private final UserService userService;
@Autowired
public UserController(UserService userService) {
this.userService = userService;
}
@PostMapping("/create")
public ResponseEntity<User> createUser(@RequestParam String name, @RequestParam String email) {
User newUser = userService.createUser(name, email);
return new ResponseEntity<>(newUser, HttpStatus.CREATED);
}
@GetMapping("/find")
public ResponseEntity<User> findUserByEmail(@RequestParam String email) {
User user = userService.getUserByEmail(email);
if (user != null) {
return new ResponseEntity<>(user, HttpStatus.OK);
} else {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}
}
-
createUser(): 새로운 사용자를 생성하여 데이터베이스에 저장합니다.
-
findUserByEmail(): 이메일로 사용자를 조회하고, 해당 사용자가 없을 경우 404 Not Found 응답을 반환합니다.