✅controller-service-repository 디자인 패턴
디자인 패턴 중 controller-service-repository 패턴을 사용해서 클래스를 만들고, 저번에 연결했던 mysql에 API를 사용해서 데이터가 저장되도록 해 보았다.
컨트롤러의 경우, @RestController 어노테이션(Annotation)을 사용해서 해당 클래스를 REST 컨트롤러로 등록해 준다.
@RestController는 REST API 기능을 수행할 때 붙이고, @Controller는 REST API가 아닌, 일반 뷰를 렌더링하는 용도의 클래스에 등록한다.
또한 디자인패턴 클래스들 중 컨트롤러가 가장 먼저 요청(request)을 받게 되는데, 해당 요청을 다음 클래스인 서비스(service)로 넘겨주기 위해서, private final 타입으로 service 멤버 변수를 정의한다.
private final로 선언하는 이유는 한번 서비스 클래스와 컨트롤러 클래스가 맵핑된 뒤 그 맵핑이 바뀌지 않게 하기 위해서이다.
또한 @Autowired를 생성자에 붙여서 컨트롤러가 생성될 때 매개변수로 서비스를 받도록 한다. 서비스와 컨트롤러가 같이 생성되도록 하기 위해서이다.
그런데 스프링에서는 객체의 생성을 스프링 컨테이너에서 관리하므로, 서비스와 컨트롤러를 할당할 때 new 생성자를 쓰면 안 된다. 그냥 매개변수로 받은 서비스를 멤버 변수에 연결하면 된다.
컨트롤러 클래스 내부에는 각 메소드를 정의할 수 있는데, 메소드 하나당 하나의 API가 된다.
각 메소드에는 @GetMapping, @PostMapping 등의 어노테이션을 붙여서 해당 API가 어떤 HTTP 방식으로 작동할지를 명시한다. 또한 @GetMapping의 경우 @RequestParam 어노테이션을 붙여서 쿼리 변수를 받을 수도 있다.
MemberController.java
@RestController
public class MemberController {
private final MemberService memberService;
@Autowired
public MemberController(MemberService memberService){
this.memberService = memberService;
}
@GetMapping("/member")
public Member member(@RequestParam(value = "name", defaultValue = "user") String name){
// API logic
}
}
서비스 클래스도 마찬가지이다. 컨트롤러 클래스가 로직 가장 위단에서 요청을 처리하는 반면(통신 관련 로직 등), 서비스 클래스에는 소위 말하는 비즈니스 로직이 포함된다.
@Service 어노테이션으로 해당 클래스를 서비스 클래스로 등록한다. 마찬가지로 private final 멤버 변수로 리포지토리(repository)를 등록한 뒤, 생성자에 @Autowired를 붙여서 서비스 클래스가 생성될 때 리포지토리 클래스가 같이 생성 및 매칭되도록 한다.
MemberService.java
@Service
public class MemberService {
private final MemberRepository memberRepository;
@Autowired
public MemberService(MemberRepository memberRepository){
this.memberRepository = memberRepository;
}
public Member join(Member member){
// business logic
}
}
리포지토리 클래스의 경우 로직이 상대적으로 간단하다. 정확하게는 리포지토리 클래스가 아니라 리포지토리 인터페이스가 된다.
리포지토리 인터페이스를 만들고, 해당 인터페이스가 <도메인 클래스 타입, 도메인 클래스 id 타입>의 JpaRepository를 상속받도록 한다.
해당 JpaRepository 안에는 DB에 접근할 때 사람들이 많이 사용하는 메소드들(findById 등)이 들어 있기 때문에, 이 인터페이스를 상속하면 해당 메소드를 별도로 정의하지 않고 사용할 수 있다.
다만 JpaRepository에서는 ID같은 PK 필드에 대해서만 findById 메소드를 제공하므로, 다른 컬럼을 기준으로 찾고 싶다면 해당 인터페이스에 추가로 메소드를 정의해주면 된다.
MemberRepository.java
@Repository
public interface MemberRepository extends JpaRepository<Member, Integer> {
Optional<Member> findByName(String name);
}
마지막으로 데이터베이스에 저장할 객체가 될 도메인(domain) 클래스를 정의한다.
PK인 필드에는 @Id를 붙이고, DB의 AUTO_INCREMENT 옵션을 적용하고 싶다면 @GeneratedValue(strategy=GenerationType.AUTO)를 적용한다.
Member.java
@Entity
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String name;
}
이후 실행하면 다음과 같이 생성된 객체에 대한 json이 잘 나오는 것을 볼 수 있다.
또한 mysql에도 데이터가 잘 저장된다.
참고한 포스트
https://spring.io/guides/gs/rest-service/
https://velog.io/@leesomyoung/SpringBoot-SQLException-No-database-selected
'server-side > spring' 카테고리의 다른 글
AWS RDS: 스프링 서버와 AWS DB 연결하기 (0) | 2023.06.30 |
---|---|
Spring Security Tutorial: Basic Authentication, JWT Authentication (0) | 2023.06.25 |
JPA-Mysql 연결: Driver 연동 오류, dialect 관련 오류 (0) | 2023.06.22 |
스프링 부트 기본편 - 섹션 8. 빈 생명주기 콜백 (0) | 2022.05.31 |
스프링 부트 기본편 - 섹션 6. 컴포넌트 스캔 (0) | 2022.05.23 |