StereoType
이란?
스프링 컨테이너가 스프링 관리 컴포넌트로 식별하게 해주는 단순한 마커다. 즉, scan-auto-detection과 dependency injection을 사용하기 위해서 사용되는 가장 기본 어노테이션이다.
Stereo Type이 범용적으로 많이 사용하게 된 시키는 Spring 2.5 부터 였다. 그 이전까지는 xml 파일에 bean을 등록하여 관리 하였다. 그러나 모든 bean들을 xml 파일로 관리 하다보니 다른 보일러플레이트 코드와 함께 xml 파일만 비대해지게 될 뿐이었다. 그래서 Spring 2.0 에서는 @Repository이 등장하였고 Spring 2.5 부터는 @Component, @Controller, @Service, @Configuration 등이 등장하면서 Stereo type에 대한 정의가 범용적으로 사용하게 되었다.
@Component
빈으로 간주되어 DI 컨테이너에서 사용할 수 있게 하는 기본 어노테이션이다
@Bean
과@Component
는 어떤 차이가 있는가?
@Component는 클래스 상단에 적으며 그 default로 클래스 이름이 bean의 이름이 된다. 또한 spring에서 자동으로 찾고(@ComponentScan 사용) 관리해주는 bean이다.
@Bean은 @Configuration으로 선언된 클래스 내에 있는 메소드를 정의할 때 사용한다. 이 메소드가 반환하는 객체가 bean이 되며 default로 메소드 이름이 bean의 이름이 된다.
@Controller
Dispatcher Sevlet에서 제공되며 여기서 @RequestMapping 어노테이션을 사용하여 클라이언트 요청을 특정 컨트롤러에 전달할 수 있게 해준다
@Service
Service annotation은 단순히 서비스 레이어에서 사용하는 bean이라는 것을 구분하기 위한 용도로 정의한다.
@Repository
검사 되지 않은 예외(DAO 메소드에서 발생)를 Spring DataAccessException으로 변환 할 수 있게 해준다.
@Configuration
한 개 이상의 @Bean 어노테이션으로 정의한 bean 들을 생성하려 할 때 클래스에 정의하는 어노테이션이다.
@Bean
은 언제 사용하는게 좋을까?
개발자가 직접 제어가 불가능한 라이브러리를 활용할 때 사용
초기에 설정을 하기 위해 활용할 때 사용
ioc 컨테이너가 만들어놓은 코드에서 빈을 검색해서 필요한 순간순간에 제공하는 것 => 스프링프레임워크의 핵심 기능
이때 이 빈들을 함수, 클래스 단위로 정의가능
클래스 단위로 정의하기 위한 것들이 구현돼있는 패키지 이름이 Spring Stereotypes
@EnableAutoConfiguration
@ComponentScan
@SpringBootApplication
public class CrudApplication {
public static void main(String[] args) {
SpringApplication.run(CrudApplication.class, args);
}
}
-> 이때 @SpringBootApplication
이라는 아이는 아래와 같은 설명을 가짐
![]
-> 이를 더 자세히 살펴본다면 아래와 같은데 이때 빨간색 밑줄 친 아이 두개를 같이 사용하게 된다면
-> 여러 패키지가 존재하는데 이 중 어떤 패키지를 사용을 해서, 걔네들을 ioc 컨테이너에 등록할 지 여부 지정해주는 annotation
-> 다만 스프링 부트에서는 마이크로 서비스 아키텍처, 도커 이미지화에 더 유리해서 굳이 검색하지 않을 패키지를 콤포넌트해줄 이유가 없어서 딱히 하지 않을 것
Component Scan : 안에 패키지를 검색할 수 있도록 지정해주는 것이다.
1. public @interface Component 살피기
-> 이 친구는 controller의 상위
-> stereotype (package org.springframework.stereotype;
)패키지 안에 자리잡고 있는 녀석이다
우리가 사용했던 Controller들도 사실상 Component의 일종이라고 볼 수 있는 것
또한 컨트롤러말고도 다른 여러가지 만들기 가능
Component가 붙은 클래스들을 검색해서 얘를 스프링컨테이너에 등록해주는 것이 Compoenet 어노테이션의 역할, 스프링 컨테이너의 관리 하에 있게 해준다
(+) Restcontroller
=> 어플리케이션에서 어떤애가 무슨 역할 하는지는 모르지만 스프링 관리 하에 두고싶다고 한다면 component interface
=> request endpoint를 만들고 싶을 땐 controller interface
package dev.dongyun.crud.post;
import java.util.List;
public interface iPostRepository {
boolean save(PostDto dto);
PostDto findById(int id); //id를 주게 되면 PostDto가 돌아가게 된다
List<PostDto> findAll();
boolean update(PostDto dto);
boolean delete(int id);
}
package dev.dongyun.crud.post;
//포스트 메모리지만 메모리 뒤에 구현된다
public class PostinMemoryRepository implements iPostRepository{
}
package dev.dongyun.crud.post;
import java.util.List;
//포스트 메모리지만 메모리 뒤에 구현된다
public class PostinMemoryRepository implements iPostRepository{
@Override
public boolean save(PostDto dto) {
return false;
}
@Override
public PostDto findById(int id) {
return null;
}
@Override
public List<PostDto> findAll() {
return null;
}
@Override
public boolean update(PostDto dto) {
return false;
}
@Override
public boolean delete(int id) {
return false;
}
}
package dev.dongyun.crud.post;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Repository;
import java.util.ArrayList;
import java.util.List;
@Repository
//포스트 메모리지만 메모리 뒤에 구현된다
public class PostinMemoryRepository implements iPostRepository{
private static final Logger logger = LoggerFactory.getLogger(PostinMemoryRepository.class);
private final List<PostDto>postList;
public PostinMemoryRepository(){
this.postList = new ArrayList<>();
}
@Override
public boolean save(PostDto dto) {
return this.postList.add(dto);
//-> add가 애초에 boolean값을 돌려준
//return true;
}
@Override
public PostDto findById(int id) {
return this.postList.get(id);
}
@Override
public List<PostDto> findAll() {
return this.postList;
}
@Override
public boolean update(int id, PostDto dto) {
PostDto targetPost = this.postList.get(id); //업데이트를 위한 목적 타겟
if (dto.getTitle()!=null){
targetPost.setTitle(dto.getTitle());
}
if (dto.getContent()!=null){
targetPost.setContent(dto.getContent());
}
this.postList.set(id, targetPost);
return true;
}
//return 값은 성공 여부 알려주는 것
@Override
public boolean delete(int id) {
this.postList.remove(id);
return true;
}
}
1) postservice 인터페이스 구현
레포지토리랑 서비스
데이터를 회수해서 그것을 확인하는 것 -> 레포지토리 (데이터 회수)
회수된 데이터에서 현재 요청을 보내는 사용자의 권한 여부 체크 -> 서비스
(+) 서비스는 실제 데이터 사용 전 검증 거칠 때 used
(+)아래 설명 출처 블로그
- @Contoller 어노테이션을 붙이면 핸들러가 스캔할 수 있는 빈(Bean) 객체가 되어 서블릿용 컨테이너에 생성됩니다. 마찬가지로 @Repository, @service 어노테이션은 해당 클래스를 루트 컨테이너에 빈(Bean) 객체로 생성해주는 어노테이션입니다.
- 둘 다 Bean 객체를 생성해주고 딱히 다른 기능을 넣어주는게 아니라서 뭘 써도 상관 없긴한데 명시적으로 구분해주기 위해 각자 분리해서 사용합니다. 부모 어노테이션인 @Component를 붙여줘도 똑같이 루트 컨테이너에 생성되지만 가시성이 떨어지기 때문에 잘 사용하지 않습니다.
- 참고로 객체 내에서 데이터 변경 작업이 있는 VO(DTO) 객체와 같은 경우는 동기화 문제로 인해 Bean 객체로 사용하지 않습니다. Bean 객체는 항상 데이터 변경이 없는 객체에 한해 사용하는 점에 유의해야 합니다.
- 컨트롤러 :
@Controller
(프레젠테이션 레이어, 웹 요청과 응답을 처리함)- 로직 처리 :
@Service
(서비스 레이어, 내부에서 자바 로직을 처리함)- 외부I/O 처리 :
@Repository
(퍼시스턴스 레이어, DB나 파일같은 외부 I/O 작업을 처리함)
package dev.d~n.crud.post;
import java.util.List;
public interface PostService {
void createPost(PostDto dto);
List<PostDto> readPostAll();
PostDto readPost(int id);
void updatePost(int id, PostDto dto);
void deletePost(int id);
}
2) postservice 인터페이스 사용할 postservicesimple 클래스 만듦
package dev.dongyun.crud.post;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
public class PostServiceimple implements PostService {
private static final Logger logger = LoggerFactory.getLogger(PostServiceimple.class);
private final iPostRepository postRepository;
//레포지토리 in이 아닌
//우리는 얘 (인터페이스) 이후, 뒤의 구현이 어떻게 되어있는지 상관않겠다는 뜻
public PostServiceimple(iPostRepository postRepository){
//여기에 autowired annotation을 붙여줘서 레포지토리 중 알맞은 레포지토리 찾게 해줘야 함
this.postRepository = postRepository;
}
@Override
public void createPost(PostDto dto) {
}
@Override
public List<PostDto> readPostAll() {
return null;
}
@Override
public PostDto readPost(int id) {
return null;
}
@Override
public void updatePost(int id, PostDto dto) {
}
@Override
public void deletePost(int id) {
}
}
package dev.dongyun.crud.post;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class PostServiceimple implements PostService {
private static final Logger logger = LoggerFactory.getLogger(PostServiceimple.class);
private final iPostRepository postRepository;
public PostServiceimple(
@Autowired iPostRepository postRepository
) {
this.postRepository = postRepository;
}
@Override
public List<PostDto> readPostAll() {
return this.postRepository.findAll();
}
@Override
public void createPost(PostDto dto) {
if(!this.postRepository.save(dto)){
throw new RuntimeException(("save failed"));
}
this.postRepository.save(dto);
}
@Override
public PostDto readPost(int id) {
return this.postRepository.findById(id);
}
@Override
public void updatePost(int id, PostDto dto) {
this.postRepository.update(id,dto);
}
@Override
public void deletePost(int id) {
this.postRepository.delete(id);
}
}
아래 3번 부분은 무시, Deprecated의 개념 용도만 체크
3) 그리고 이제 우리는 postcontroller가 아닌 restcontroller 사용할 예정
-> 따라서 post rest controller 위에 @Deprecated 붙여주기
//Deprecated는 당장 지원은 해주지만 곧 쓰지 못할 버전이라고 알려주는 것
//오로지 응답을 받고 요청을 보내는 역할로만
@Deprecated
그럼 이렇게 빗금 처리됨 & 더 이상 아래 항목들 관리 진행하지 않음
public PostServiceimple(
@Autowired iPostRepository postRepository
) {
this.postRepository = postRepository;
}
-> iPostRepository인터페이스에 해당하는 아이들을 ioc가 찾아서 강제로 넣어주는 것- Dependency injection 이라는 특징이었지
4) 이제 service와 postcontroller 연결해서 사용
(+)참고 postservice interface
package dev.dongyun.crud.post;
import java.util.List;
public interface PostService {
void createPost(PostDto dto);
List<PostDto> readPostAll();
PostDto readPost(int id);
void updatePost(int id, PostDto dto);
void deletePost(int id);
}
기존에 지정해뒀던 postlist -> postservice의 메소드 호출 방식으로 진행
package dev.dongyun.crud.post;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
//Deprecated는 당장 지원은 해주지만 곧 쓰지 못할 버전이라고 알려주는 것
//오로지 응답을 받고 요청을 보내는 역할로만
@Controller
@ResponseBody
//이렇게 붙여놓으면
//클래스 안 모든 함수들이 responsebody가
// 붙은 형태로 함수 선언 완료
@RequestMapping("post")
public class PostController {
private static final Logger logger = LoggerFactory.getLogger(PostController.class);
private final PostService postservice;
public PostController(
@Autowired PostService postservice) {
this.postservice = postservice;
//왜 위에는 list고 아래는 arraylist냐
// list는 인터페이스, arraylist는 구현체
}
@PostMapping("create")
public void createPost(@RequestBody PostDto postDto){
logger.info(postDto.toString());
this.postservice.createPost(postDto);
}
@GetMapping("read-all")
public List<PostDto>readPostAll(){
logger.info("read all");
return this.postservice.readPostAll();
}
@GetMapping("read-one")
public PostDto readPostOne(@RequestParam("id") int id){
logger.info("read one");
return this.postservice.readPost(id);
}
@PatchMapping("update")
public void updatePost(
@RequestParam("id") int id,
@RequestBody PostDto postDto //포스트 요청의 body
){
PostDto targetPost = this.postservice.readPost(id); //업데이트를 위한 목적 타겟
if (postDto.getTitle()!=null){
targetPost.setTitle(postDto.getTitle());
}
if (postDto.getContent()!=null){
targetPost.setContent(postDto.getContent());
}
this.postservice.updatePost(id, targetPost);
}
@DeleteMapping("delete")
public void deletePost(@RequestParam("id") int id){
this.postservice.deletePost(id);
}
}
5) RestController도 postlist 없애고 postservice 다루는 방향으로 고쳐봅세~~
package dev.dongyun.crud.post;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
@RestController
@RequestMapping("post")
public class PostRestController {
private static final Logger logger = LoggerFactory.getLogger(PostRestController.class);
private final PostService postservice;
public PostRestController(
@Autowired PostService postservice
){
this.postservice = postservice;
}
@PostMapping()
@ResponseStatus(HttpStatus.CREATED)
public void createPost(@RequestBody PostDto postDto){
logger.info(postDto.toString());
this.postservice.createPost(postDto);
}
@GetMapping()
public List<PostDto> readPostAll(){
logger.info("read-all");
return this.postservice.readPostAll();
}
//GET
//GET /post?id=0
@GetMapping("{id}}")
public PostDto readPost(@PathVariable("id") int id){
logger.info("in read post");
return this.postservice.readPost(id);
}
@PutMapping("{id}")
public void updatePost(
@PathVariable("id") int id,
@RequestBody PostDto postDto
){
PostDto targetPost = this.postservice.readPost(id); //업데이트를 위한 목적 타겟
if (postDto.getTitle()!=null){
targetPost.setTitle(postDto.getTitle());
}
if (postDto.getContent()!=null){
targetPost.setContent(postDto.getContent());
}
this.postservice.updatePost(id, targetPost);
}
@DeleteMapping("{id}")
public void deletePost(@PathVariable("{id}") int id){
this.postservice.deletePost(id);
}
}
@PostMapping()
@ResponseStatus(HttpStatus.CREATED)
public void createPost(@RequestBody PostDto postDto){
logger.info(postDto.toString());
this.postservice.createPost(postDto);
}
=> HttpServelet 추가적으로 넣기
public void createPost(@RequestBody PostDto postDto, HttpServletRequest request){
logger.info(postDto.toString());
this.postservice.createPost(postDto);
}
=> HttpServlet의 구조는 아래와 같음 (설명,이미지출처)
@PostMapping()
@ResponseStatus(HttpStatus.CREATED)
public void createPost(@RequestBody PostDto postDto, HttpServletRequest request){
logger.info("content");
logger.info(postDto.toString());
logger.info("full request");
logger.info(String.valueOf(request));
logger.info("header of request");
logger.info(request.getHeader("Content-type"));
this.postservice.createPost(postDto);
}
=> 위와 같이 request는 json 형태로 온다는 걸 알 수 있고
logger.info("method of request");
logger.info(request.getMethod());
그 외에도 request에서 얻어오는 함수들 (이미지, 설명 출처 블로그)
1) Parameter 0 of constructor in dev.dongyun.crud.post.PostController required a bean of type 'dev.dongyun.crud.post.PostService' that could not be found.Action:
Consider defining a bean of type 'dev.dongyun.crud.post.PostService' in your configuration.
=> bean이 지정되지 않았단다 -> 이는 적절한 어노테이션을 붙여주지 않았다는 말이지
실제로 PostService 인터페이스를 상속받는 구현체아잉 들어가보니 어노테이션 부분이 깨끗..^^
2) PostService와 같은 인터페이스 새로 선언 시에
=> private final PostService postservice;
코드 추가
@RestController
@RequestMapping("post")
public class PostRestController {
private static final Logger logger = LoggerFactory.getLogger(PostRestController.class);
private final List<PostDto> postList;
private final PostService postservice;
public PostRestController(){
this.postList = new ArrayList<>();
}
-> 그럼 해당 줄에 빨간 줄 뜰텐데, 이때 아래와 가팅 autowired 써서 생성자 수정해주면 됨
public class PostRestController {
private static final Logger logger = LoggerFactory.getLogger(PostRestController.class);
private final PostService postservice;
public PostRestController(
@Autowired PostService postservice
){
this.postservice = postservice;
}
(+)
postman에서 postcontroller 따로, restcontroller 따로 테스트해보고 싶을 떈 requestmapping 죽이고 살리는거 선택함 따라서