1. Service : 기능 제공
- 1 : 1 매칭이 안 됨 → 메서드를 조합해서 사용하는 곳
- Controller : API 주소를 관리
2. Service Layer
- 소프트웨어의 아키텍처에서 비즈니스 로직을 처리하는 부분을 담당하는 계층
- 비즈니스 로직 처리 : 사용자의 요청을 받아들이고, 해당 요청에 대한 비즈니스 로직을 처리
- 트랜잭션 관리 : 하나의 트랜잭션으로 묶여야 하는 경우, 서비스 레이어에서 트랜잭션을 관리
데이터의 일관성과 무결성을 유지
- 영속성 관리 : 데이터의 영속성을 관리
생성, 읽기, 업데이트, 삭제(CRUD) 작업을 수행하고, DB와의 상호작용을 담당
- 로직의 재사용성 : 공통적으로 사용되는 로직을 추상화하여 재사용
코드의 중복을 줄이고 유지 보수성 향상
3. Service 만들기
- @Service : IoC에 등록
- 컨트롤러는 서비스가, 서비스는 레파지토리가 필요함/ 의존 관계
- @Transactional : JPA 레파지토리가 아니라 호출하는 서비스가 가지고 있어야 함
- save하다가 오류가 여러 번 발생할 수 있음
package shop.mtcoding.blog.user; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @RequiredArgsConstructor @Service // IoC에 등록 public class UserService { // 컨트롤러는 서비스가, 서비스는 레파지토리가 필요함 - 의존 관계 private final UserJPARepository userJPARepository; @Transactional // JPA 레파지토리가 아니라 호출하는 서비스 가지고 있어야 함 public void join(UserRequest.JoinDTO reqDTO){ userJPARepository.save(reqDTO.toEntity()); } }
4. UserJPARepository에 save() 수정하기
- Test에서는 트랜잭션이 있기 때문에 따로 어노테이션 사용 안했음!
- 단건 조회할 때 null로 오류가 날 수 있는 경우 Optional 걸어놓기!
package shop.mtcoding.blog.user; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import java.util.Optional; // 자동 컴포넌트 스캔이 됨 -> @Reposirotry를 하지 않아도 메모리에 뜸 public interface UserJPARepository extends JpaRepository<User, Integer> { // 인터페이스는 인터페이스를 상속(extends)할 수 있음 // 간단한 쿼리 작성하기(join 가능) // @Query("select u from User u where u.username = :username and u.password = :password") // 쿼리 메서드 -> 복잡도만 올라감, 그냥 쿼리 작성하기 Optional<User> findByUsernameAndPassword(@Param("username")String username, @Param("password") String password); Optional<User> findByUsername(@Param("username")String username); }
- BoardJPARepository 에도 Optional 추가
package shop.mtcoding.blog.board; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import java.util.Optional; public interface BoardJPARepository extends JpaRepository<Board, Integer> { @Query("select b from Board b join fetch b.user u where b.id = :id") Optional<Board> findByIdJoinUser(@Param("id") int id); }
5. UserController 에 join 수정하기
package shop.mtcoding.blog.user; import com.sun.source.tree.TryTree; import jakarta.persistence.NoResultException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpSession; import lombok.RequiredArgsConstructor; import org.springframework.dao.DataIntegrityViolationException; import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import shop.mtcoding.blog._core.errors.exception.Exception400; import shop.mtcoding.blog._core.errors.exception.Exception401; @RequiredArgsConstructor @Controller public class UserController { private final UserRepository userRepository; private final UserService userService; private final HttpSession session; @PostMapping("/join") public String join(UserRequest.JoinDTO reqDTO) { userService.join(reqDTO); return "redirect:/"; } @PostMapping("/login") public String login(UserRequest.LoginDTO reqDTO) { try{ User sessionUser = userRepository.findByUsernameAndPassword(reqDTO); session.setAttribute("sessionUser", sessionUser); return "redirect:/"; }catch (EmptyResultDataAccessException e) { throw new Exception401("유저네임 혹은 비밀번호가 틀렸어요"); } } @GetMapping("/join-form") public String joinForm() { return "user/join-form"; } @GetMapping("/login-form") public String loginForm() { return "user/login-form"; } @GetMapping("/user/update-form") // session(mypage)에 있으니 id가 필요 없음 public String updateForm(HttpServletRequest request) { User sessionUser = (User) session.getAttribute("sessionUser"); // 절대 nu User user = userRepository.findById(sessionUser.getId()); // 없어도 상관은 없음 request.setAttribute("user", user); return "user/update-form"; } @GetMapping("/logout") public String logout() { session.invalidate(); return "redirect:/"; } }
6. UserService 에 Join 수정하기
- 잡을 수 있는 오류는 잡아야 함!
package shop.mtcoding.blog.user; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import shop.mtcoding.blog._core.errors.exception.Exception400; import java.util.Optional; @RequiredArgsConstructor @Service // IoC에 등록 public class UserService { // 컨트롤러는 서비스가, 서비스는 레파지토리가 필요함 - 의존 관계 private final UserJPARepository userJPARepository; @Transactional // JPA 레파지토리가 아니라 호출하는 서비스가 가지고 있어야 함 public void join(UserRequest.JoinDTO reqDTO) { // 1. 유효성 검사(컨트롤러 책임) // 2. 유저네임 중복검사(서비스 체크) - DB 연결이 필요함 // 기존의 유저네임을 조회 Optional<User> userOp = userJPARepository.findByUsername(reqDTO.getUsername()); if (userOp.isPresent()){ throw new Exception400("중복된 유저네임입니다"); } userJPARepository.save(reqDTO.toEntity()); } }
- 중복되지않은 아이디의 경우 회원가입, 로그인 모두 정상!
7. UserService 에 Login 만들기
package shop.mtcoding.blog.user; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import shop.mtcoding.blog._core.errors.exception.Exception400; import shop.mtcoding.blog._core.errors.exception.Exception401; import java.util.Optional; @RequiredArgsConstructor @Service // IoC에 등록 public class UserService { // 컨트롤러는 서비스가, 서비스는 레파지토리가 필요함 - 의존 관계 private final UserJPARepository userJPARepository; @Transactional // JPA 레파지토리가 아니라 호출하는 서비스가 가지고 있어야 함 public User login(UserRequest.LoginDTO reqDTO) { // 1. 유효성 검사(컨트롤러 책임) // 2. 유저네임 중복검사(서비스 체크) - DB 연결이 필요함 // 3. hash 값 비교 // 조회 -> null이면 throw를 날리고 아니면 값을 받음 User sessionUser = userJPARepository.findByUsernameAndPassword(reqDTO.getUsername(), reqDTO.getPassword()) .orElseThrow(() -> new Exception401("인증이 되지 않았습니다")); return sessionUser; } @Transactional // JPA 레파지토리가 아니라 호출하는 서비스가 가지고 있어야 함 public void join(UserRequest.JoinDTO reqDTO) { // 1. 유효성 검사(컨트롤러 책임) // 2. 유저네임 중복검사(서비스 체크) - DB 연결이 필요함 // 기존의 유저네임을 조회 Optional<User> userOp = userJPARepository.findByUsername(reqDTO.getUsername()); if (userOp.isPresent()){ throw new Exception400("중복된 유저네임입니다"); } userJPARepository.save(reqDTO.toEntity()); } }
8. UserController 에 login 수정하기
package shop.mtcoding.blog.user; import com.sun.source.tree.TryTree; import jakarta.persistence.NoResultException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpSession; import lombok.RequiredArgsConstructor; import org.springframework.dao.DataIntegrityViolationException; import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import shop.mtcoding.blog._core.errors.exception.Exception400; import shop.mtcoding.blog._core.errors.exception.Exception401; @RequiredArgsConstructor @Controller public class UserController { private final UserRepository userRepository; private final UserService userService; private final HttpSession session; @PostMapping("/user/update") public String update(UserRequest.UpdateDTO reqDTO){ User sessionUser = (User) session.getAttribute("sessionUser"); User newSessionUser = userRepository.updateById(sessionUser.getId(), reqDTO.getPassword(), reqDTO.getEmail()); session.setAttribute("sessionUser", newSessionUser); return "redirect:/"; } @PostMapping("/join") public String join(UserRequest.JoinDTO reqDTO) { userService.join(reqDTO); return "redirect:/"; } @PostMapping("/login") public String login(UserRequest.LoginDTO reqDTO) { User sessionUser = userService.login(reqDTO); session.setAttribute("sessionUser", sessionUser); return "redirect:/"; } @GetMapping("/join-form") public String joinForm() { return "user/join-form"; } @GetMapping("/login-form") public String loginForm() { return "user/login-form"; } @GetMapping("/user/update-form") // session(mypage)에 있으니 id가 필요 없음 public String updateForm(HttpServletRequest request) { User sessionUser = (User) session.getAttribute("sessionUser"); // 절대 nu User user = userRepository.findById(sessionUser.getId()); // 없어도 상관은 없음 request.setAttribute("user", user); return "user/update-form"; } @GetMapping("/logout") public String logout() { session.invalidate(); return "redirect:/"; } }
- 서비스 안에서 session 설정 가능
- 정상적인 로그인은 잘 작동함
Share article