@Repository? @Service?

3 λΆ„ μ†Œμš”

Repository? Service?

Spring Framework μ—μ„œλŠ” IoC μ»¨ν…Œμ΄λ„ˆμ— Context λ₯Ό λ“±λ‘ν•˜λŠ” μ—¬λŸ¬κ°€μ§€ 방법이 μžˆλ‹€. Xml Config 등둝, @Configuration 등둝, κ΅¬ν˜„ ν΄λž˜μŠ€μ— @Component , @Service, @Repository λͺ…μ‹œ λ“±λ“±(Component, Service, Repository μ°¨μ΄λŠ” 링크 κΈ€ μ°Έμ‘°) 참쑰된 κΈ€μ—μ„œ μ„€λͺ…ν•˜λ‹€ μ‹œν”Ό, μ–΄λŠ μ–΄λ…Έν…Œμ΄μ…˜μ„ μ‚¬μš©ν•˜λ˜κ°„μ—, org.springframework.stereotype νŒ¨ν‚€μ§€μ˜ μ–΄λ…Έν…Œμ΄μ…˜μ€ IoC μ»¨ν…Œμ΄λ„ˆμ— 클래슀λ₯Ό Context 둜 λ“±λ‘ν•΄μ£ΌλŠ” κΈ°λŠ₯을 ν•œλ‹€.

μ‹€μ œλ‘œ, org.springframework.stereotype νŒ¨ν‚€μ§€μ˜ @Component , @Service, @Repository, @Controller μ–΄λ…Έν…Œμ΄μ…˜ κ΅¬ν˜„ μ½”λ“œλ₯Ό 보면 λ‹€μŒκ³Ό κ°™λ‹€.

  @Target(ElementType.TYPE)
  @Retention(RetentionPolicy.RUNTIME)
  @Documented
  @Indexed
  public @interface Component {
  
    String value() default "";
  }
  @Target({ElementType.TYPE})
  @Retention(RetentionPolicy.RUNTIME)
  @Documented
  @Component
  public @interface Controller {
  
    @AliasFor(annotation = Component.class)
    String value() default "";
  }
  @Target({ElementType.TYPE})
  @Retention(RetentionPolicy.RUNTIME)
  @Documented
  @Component
  public @interface Service {
  
    @AliasFor(annotation = Component.class)
    String value() default "";
  }
  @Target({ElementType.TYPE})
  @Retention(RetentionPolicy.RUNTIME)
  @Documented
  @Component
  public @interface Repository {
  
    @AliasFor(annotation = Component.class)
    String value() default "";
  }

Controller, Service, Repository μ–΄λ…Έν…Œμ΄μ…˜ λͺ¨λ‘, @Component μ–΄λ…Έν…Œμ΄μ…˜μ΄ λͺ…μ‹œ λ˜μ–΄μžˆμŒμ„ 확인 ν•  수 있으며, @AliasFor 둜 λ¬Άμ—¬μžˆμŒμ„ 확인 ν•  수 μžˆλ‹€. μ΄λŠ” 곧 μ„Έ μ–΄λ…Έν…Œμ΄μ…˜ λͺ¨λ‘ @Component μ–΄λ…Έν…Œμ΄μ…˜κ³Ό 같은 κΈ°λŠ₯을 ν•œλ‹€λŠ”κ²ƒμ„ 확인 ν•  수 μžˆμ—ˆλ‹€. 즉 μ„Έ μ»΄ν¬λ„ŒνŠΈμ˜ μ°¨μ΄λŠ” κ·Έμ € @Component에 역할을 λͺ…μ‹œν•˜λŠ”κ²ƒμ΄ μ–΄λ…Έν…Œμ΄μ…˜λ“€μ˜ 역할이라고 이해 ν•  수 μžˆλ‹€.

β€œκ·ΈλŸΌ RepositoryλŠ” μ–΄λŠ ν΄λž˜μŠ€μ— λͺ…μ‹œν•˜κ³ , ServiceλŠ” μ–΄λŠ ν΄λž˜μŠ€μ— λͺ…μ‹œν•΄μ•Ό ν•˜λ‚˜μš”? πŸ€”β€

맀우 ν›Œλ₯­ν•œ 질문이라고 ν•  수 μžˆλ‹€. 사싀 ν•„μžκ°€ μŠ€ν”„λ§ ν”„λ ˆμž„μ›Œν¬μ— λŒ€ν•œ 이해도가 μ μ—ˆμ„ λ‹Ήμ‹œμ—λŠ”, κΈ°λŠ₯적으둜 차이가 μ—†μœΌλ‹ˆ μ „λΆ€ @Component μ–΄λ…Έν…Œμ΄μ…˜μœΌλ‘œ λͺ…μ‹œν•˜μ—¬ IoC μ»¨ν…Œμ΄λ„ˆμ— Contextλ₯Ό λ“±λ‘ν•˜μ˜€λ‹€. ν•˜μ§€λ§Œ κ°œλ°œμ€ ν˜Όμžμ„œ ν•˜λŠ”κ²ƒμ΄ μ•„λ‹ˆλ©°, μž…μœΌλ‘œ μ£Όκ³ λ°›λŠ” λŒ€ν™”λ³΄λ‹€ μ½”λ“œλ‘œ μ£Όκ³ λ°›λŠ” λŒ€ν™”κ°€ 훨씬 μ€‘μš”ν•˜λ―€λ‘œ, λͺ…μ‹œ λΌλŠ” κ°œλ…μ΄ ꡉμž₯히 μ€‘μš”ν•˜λ‹€.

λ‹¨μˆœλͺ…λ£Œν•˜κ²Œ 말 ν•˜μžλ©΄, Repository λŠ” 말 κ·ΈλŒ€λ‘œ, Resource λ₯Ό μ£Όκ³ λ°›λŠ” 역할을 ν•˜λŠ” μΈν„°νŽ˜μ΄μŠ€μ— λͺ…μ‹œν•˜μ—¬μ•Ό ν•œλ‹€. λŒ€ν‘œμ μœΌλ‘œλŠ” DB CRUD의 μ—”λ“œν¬μΈνŠΈλ₯Ό ν•˜λŠ” μΈν„°νŽ˜μ΄μŠ€μ— λͺ…μ‹œν•œλ‹€. λŒ€ν‘œμ μœΌλ‘œ MyBatis 와 같은 SQLMapper ν”„λ ˆμž„μ›Œν¬λ₯Ό μ—°λ™ν•˜μ—¬ μ‚¬μš© ν•  λ•Œ λ‹€μŒκ³Ό 같은 Repsository Context듀이 많이 μ‘΄μž¬ν•œλ‹€.

  @Repository
  public interface UserRepository {

    @Select("SELECT * FROM user WHERE USER_ID = #{userId}")                       
    Optional<User> findByUserId(String userId);

    @Update("UPDATE user SET USER_NAME = #{userName}, PASSWORD = #{password} WHERE USER_ID = #{userId}")                       
    User updateUser(User user);
  
    @Delete("DELETE FROM user WHERE USER_ID = #{userId}")
    void deleteUserByUserId(String userId);

    @Insert("INSERT INTO user(USER_ID, USER_NAME, PASSWORD) VALUES (#{userId}, #{userName}, #{password})")
    User createUser(User user);
  }

ν•΄λ‹Ή μΈν„°νŽ˜μ΄μŠ€λŠ” User λΌλŠ” ν…Œμ΄λΈ”μ˜ 값을 κ°€μ Έμ˜€κ±°λ‚˜, μƒμ„±ν•˜λŠ” μ €μž₯μ†Œμ˜ 역할을 ν•œλ‹€. λ‹¨μˆœν•˜κ²Œ μ €μž₯ν•˜κ³ , κ°€μ Έμ˜€λŠ” μ—­ν• λ§Œ μˆ˜ν–‰ν•˜λŠ” 것이닀. 즉 둜직이 μ‘΄μž¬ν•΄μ„œλŠ” μ•ˆλœλ‹€.

Repository 와 κ΅¬ν˜„ λΉ„μ¦ˆλ‹ˆμŠ€ λ‘œμ§κ°„μ˜ μ€‘κ°œμž 역할을 ν•΄ μ£ΌλŠ” 것이, Service 의 역할이닀. (λ¬Όλ‘  Service λŠ” λ‹€λ₯Έ μ—¬λŸ¬ κΈ°λŠ₯을 μ œκ³΅ν•˜λŠ” 갖가지 Context둜 κ΅¬ν˜„λ  수 μžˆλ‹€. μ§€κΈˆμ€ DB CRUD κΈ°μ€€μœΌλ‘œ μ„€λͺ…ν•œλ‹€.)

  public interface UserService {
  	
    Optional<User> getUserById(String userId);

    User createUser(User user);

    User updateUser(User user);

    void deleteUser(String userId);
  
  }
  @Service
  public class UserServiceImpl implements UserService {
  
      private final UserRepository repository;
  
      public UserServiceImpl(UserRepository repository) {
  
          this.repository = repository;
      }
  
      @Override
      public Optional<User> getUserById(String userId) {
  
          return repository.findByUserId(userId);
      }
  
      @Override
      public User createUser(User user) {
  
          repository.createUser(user);
      }
  
      @Override
      public User updateUser(User user) {
  
          return repository.updateUser(user);
      }
  
      @Override
      public void deleteUser(String userId) {
  
          repository.deleteUserByUserId(userId);
      }
  }

λŒ€κ°• μ΄λ ‡κ²Œ Service κ°€ κ΅¬ν˜„λ˜μ–΄, λΉ„μ¦ˆλ‹ˆμŠ€ λ‘œμ§μ—μ„œ μ‚¬μš©λœλ‹€. μ΄λ ‡κ²Œλ§Œ 놓고 보면, 문득 이런 생각이 λ“€ 수 μžˆλ‹€.

β€œκ·Έλƒ₯ Repository에 μžˆλŠ” λ©”μ„œλ“œ κ°–λ‹€ μ“΄κ±° μ•„λ‹ˆμ•Ό? 이 짓거리λ₯Ό μ™œ ν•΄?”

ν•„μžκ°€ Spring Framework λ₯Ό ν™œμš©ν•΄ 처음 κ°œλ°œμ„ ν–ˆμ„ λ•Œ, κ°€μž₯ λ¨Όμ € λ“€μ—ˆλ˜ 생각이닀. μ•„λž˜μ˜ 예제λ₯Ό 톡해 ν•΄λ‹Ή νŒ¨ν„΄μ΄ κ°œλ°œμ„ ν•˜λ©΄μ„œ μ–΄λ–€ 이득을 얻을 수 μžˆλŠ”μ§€ 확인 ν•΄ 보자.

  @RestController
  @RequiredArgsConstructor
  public class Controller {
  
    private final UserService userService;
  
    @RequestMapping(method = RequestMethod.POST, path = "/createUser", params = { "userId", "userName", "password" })
    public ResponseEntity<User> createUser(@RequestParam String userId, @RequestParam String userName, @RequestParam String password) throws ResponseStatusException {
  
      // κΈˆμ§€λœ USER_ID둜 생성 μš”μ²­μ΄ 듀어왔을 경우 필터링
      if (IllegalUserList.contains(userId)) {
        throw new ResponseStatusException(HttpStatus.BAD_REQUEST, String.format("Create User Failed, Illegal userId : [%s]", userId));
      }
  
      User user = new User(userId, userName, password);
      return ResponseEntity.ok().body(userService.createUser(user));
       
    }
  
  }

λ§Œμ•½ μœ„μ™€ 같이, DB에 CRUDλ₯Ό ν•˜κΈ° 전에 μ‚¬μ „μž‘μ—…μ΄ ν•„μš”ν•œ κ²½μš°κ°€ λ‘œμ§μ„ κ΅¬μ„±ν•˜λ©΄μ„œ 생길 수 μžˆλ‹€. 별 λ¬Έμ œκ°€ μ—†μ–΄ λ³΄μ΄λŠ” λ‘œμ§μ΄μ§€λ§Œ, createUser() λ©”μ„œλ“œλŠ” ν•΄λ‹Ή μ»¨νŠΈλ‘€λŸ¬μ—μ„œλ§Œ μ‚¬μš©ν•˜λŠ”κ²ƒμ΄ μ•„λ‹Œ, λ²”μš©μ μœΌλ‘œ μ‚¬μš©λ˜λŠ” λ©”μ„œλ“œμ΄λ‹€.

λ”°λΌμ„œ μ•„λž˜μ™€ 같이 Service κ΅¬ν˜„ λ©”μ„œλ“œ λΆ€λΆ„μ—μ„œ μ‚¬μ „μž‘μ—…μ„ ν•΄μ£Όλ©΄, μ€‘λ³΅μ½”λ“œ 방지에 큰 이바지 ν•  수 μžˆλ‹€. ν•΄λ‹Ή 사전 μž‘μ—…μ€ 필터링이 될 μˆ˜λ„ 있고, 캐싱이 될 μˆ˜λ„ μžˆλ‹€.

  @Override
  public void createUser(User user) throw IllegalArgumentException {
  
    if (IllegalUserList.contains(userId)) {
        throw new IllegalArgumentException(String.format("Create User Failed, Illegal userId : [%s]", userId));
    }
     repository.createUser(user);
  }
  

νƒœκ·Έ: ,

μΉ΄ν…Œκ³ λ¦¬:

μ—…λ°μ΄νŠΈ:

λŒ“κΈ€λ‚¨κΈ°κΈ°