Annotation 정리

한지개·2023년 4월 3일
0

spring

목록 보기
1/7

계속 추가 예정

Annotation

  • 단순 주석 기능x, 특별한 기능을 포함함.
  • annotation을 화룡하여 Spring Framework는 해당 클래스가 어떤 역할인지 정함.
  • 프로그램에게 추가적인 정보를 제공해주는 메타데이터라고 볼 수 있음.
    (meta data : 데이터를 위한 데이터)

@Controller

  • Spring의 Controller를 의미한다.
  • Spring MVC에서 Controller클래스에 쓰인다.
  • Spring에게 해당 클래스가 Controller의 역할을 한다고 명시하기 위해 사용하는 Annotation이다.

코드

@Controller // 이 클래스는 컨트롤러 역할을 한다
// 이 클래스는 /user로 들어오는 요청을 처리한다.
@RequestMappring("/user") 
public class UserController{
	@RequestMapping(method = RequestMethod.GET)
	public String getUser(Model model){
	// GET method, /user 요청을 처리
	}
}

@RequestMapping

+@GetMapping(@RequestMapping(Method=RequestMethod.GET)과 같다)

  • 요청 url을 어떤 메서드가 처리할지 Mapping해주는Annotation이다.
  • Controller나 Controller의 메소드에 적용한다.
  • 요청을 받는 형식인 GET, POST, PATCH, PUT, DELETE를 정의하기도 한다.
  • 요청 받는 형식을 정의하지 않는다면,(위에 저것들) 자동적으로 GET으로 설정된다. (Method=RequestMethod.GET)

ex)
@RequestMappring("/list")
라고 쓰면
@RequestMappring("/list", method=RequestMethod.GET)으로 자동 설정됨

사용하는 형태

@RequestMapping("/list"); 

@RequestMapping("/home, /about");

// GET의 요청 형식으로 /admin url에서 요청이 들어옴
@RequestMapping("/admin", method=RequestMethod.GET);

@RequestMapping(value="")와 같은 형태로 작성하며, 요청 들어온 url과 annotation의 value값이 일치하면 해당 클래스나 메소드가 실행된다.

1) Class 단위에 사용하면 하위 메소드에 모두 적용된다.
2) 메소드에 적용되면 해당 메소드에서 지정한 방식으로 url을 처리한다.

코드

@Controller
// 1) Class Level
// 모든 메서드에 적용되는 경우 “/home”로 들어오는 모든 요청에 대한 처리를 해당 클래스에서 한다는 것을 의미
@RequestMapping("/home") 
public class HomeController {
    /* an HTTP GET for /home */ 
    @RequestMapping(method = RequestMethod.GET)
    public String getAllEmployees(Model model) {
        ...
    }
    /*
    2) Handler Level
    요청 url에 대해 해당 메서드에서 처리해야 되는 경우
    “/home/employees” POST 요청에 대한 처리를 addEmployee()에서 한다는 것을 의미한다.
    value: 해당 url로 요청이 들어오면 이 메서드가 수행된다.
    method: 요청 method를 명시한다. 없으면 모든 http method 형식에 대해 수행된다.
    */
    /* an HTTP POST for /home/employees */ 
    @RequestMapping(value = "/employees", method = RequestMethod.POST) 
    public String addEmployee(Employee employee) {
        ...
    }
}

(+) @RequestMapping에 대한 모든 매핑 정보는 Spring에서 제공하는 HandlerMapping Class가 가지고 있다.

@RequestParam

  • request의 parameter에서 가져오는 것이다. method의 파라미터에 사용된다.
  • ?moviename=thepurge 와 같은 쿼리 파라미터를 파싱해준다.

HTTP GET 요청에 대해 매칭되는 request parameter 값이 자동으로 들어간다.
url 뒤에 붙는 parameter 값을 가져올 때 사용한다.

http://localhost:8080/home?index=1&page=2

index=1, page=2

@GetMapping("/home")
public String show(@RequestParam("page") int pageNum) {
}

위의 경우 GET /home?index=1&page=2와 같이 uri가 전달될 때 page parameter를 받아온다.
@RequestParam 어노테이션의 괄호 안의 문자열이 전달 인자 이름(실제 값을 표시)이다.

코드

@RequestMapping(value = "/search/movie", method = RequestMethod.GET)
public ResponseEntity<?> someMethod(@RequestParam String moviename) 
{
	// request URI would be like '/search/movie?	moviename=thepurge'
	try {
   		List<Movie> movies = service.searchByMoviename(moviename);
	} catch(Exception e) {
   		e.printStackTrace();
	}
	// return some response here
}

코드

@Controller
@RequestMapping("/user")
public class UserController{
	@RequestMapping(method = RequestMethod.GET)
	public String getUser(@RequestParam String nickname, @RequestParam(name="old") String age){
		//GET method, /user의 요청을 처리
		// https://naver.com?nickname=dog&old=10
		String sub = nickname + "_" + age;
...
	}

}

@ResponseBody 와 @RequestBody

클라이언트에서 서버로 통신하는 메시지를 요청(request) 메시지라고 하며, 서버에서 클라이언트로 통신하는 메시지를 응답(response) 메시지라고 한다.

웹에서 화면전환(새로고침) 없이 이루어지는 동작들은 대부분 비동기 통신으로 이루어진다.

비동기통신을 하기위해서는 클라이언트에서 서버로 요청 메세지를 보낼 때, 본문에 데이터를 담아서 보내야 하고, 서버에서 클라이언트로 응답을 보낼때에도 본문에 데이터를 담아서 보내야 한다.

이 본문이 바로 body 이다.

즉, 요청본문 requestBody, 응답본문 responseBody 을 담아서 보내야 한다.

이때 본문에 담기는 데이터 형식은 여러가지 형태가 있겠지만 가장 대표적으로 사용되는 것이 JSON 이다.

즉, 비동기식 클라-서버 통신을 위해 JSON 형식의 데이터를 주고받는 것이다.

@ResponseBody
자바객체를 HTTP요청의 바디내용으로 매핑하여 클라이언트로 전송한다.

@ResponseBody 가 붙은 파라미터가 있으면 HTTP요청의 미디어타입과 파라미터의 타입을 먼저 확인한다.
메세지 변환기 중에서 해당 미디어타입과 파라미터 타입을 처리할 수 있다면, HTTP요청의 본문 부분을 통째로 변환해서 지정된 메소드 파라미터로 전달해준다.

즉, @Responsebody 어노테이션을 사용하면 http요청 body를 자바 객체로 전달받을 수 있다.

(+) @RestController
@Controller와는 다르게 @RestController는 리턴값에 자동으로 @ResponseBody가 붙게되어 별도 어노테이션을 명시해주지 않아도 HTTP 응답데이터(body)에 자바 객체가 매핑되어 전달 된다.

@Controller인 경우에 바디를 자바객체로 받기 위해서는 @ResponseBody 어노테이션을 반드시 명시해주어야한다.

@RequestBody / @ResponseBody 정리.

  • 클라이언트에서 서버로 필요한 데이터를 요청하기 위해 JSON 데이터를 요청 본문에 담아서 서버로 보내면, 서버에서는 @RequestBody 어노테이션을 사용하여 HTTP 요청 본문에 담긴 값들을 자바객체로 변환시켜, 객체에 저장한다.

  • 서버에서 클라이언트로 응답 데이터를 전송하기 위해 @ResponseBody 어노테이션을 사용하여 자바 객체를 HTTP 응답 본문의 객체로 변환하여 클라이언트로 전송한다.

  • @ResponseBody는 메소드에서 리턴되는 값이 view로 출력되지 않고 HTTP Response Body에 직접 쓰여지게 됩니다. reture시에 json, xml과 같은 데이터를 return 한다

코드

@Controller
@RequestMapping("/user")
public class UserController{
	@RequestMapping(method = RequestMethod.GET)
	@ResponseBody
	public String getUser(@RequestParam String nickname, @RequestParam(name="old") String age){
		//GET method, /user의 요청을 처리
		// https://naver.com?nickname=dog&old=10
		User user = new User();
		user.setName(nickName);
		user.setAge(age);
		return user;
	}

}

@AfterEach

  • 메서드가 실행이 끝날때마다 어떤 동작을 수행하는 것이다.
  • 콜백메서드라고 한다.

모든 테스트는 순서가 보장되지 않는다.

때문에 모든 메서드는 순서상관없이 따로 동작하게 만들어야한다.

findAll()에서 객체 2개가 저장됐기 때문에 findbyname()에서 실행할때 이전에 저장된 객체가 나와버려서 오류가 난다.

때문에 MemoryMemberRepositoryTest에 test가 끝날때마다 Repository를 지워주는 메소드를 만들어야 한다.

때문에 @AfterEach를 사용해야 한다.

ex) save() -> afterEach() -> findbyname() -> afterEach() -> ...

하나의 테스트가 끝날때마다 저장소나 공용데이터를 지워줘야 한다.

! 서로 의존관계없이 설계해야한다. !

한번에 여러 테스트를 실행하면 메모리 DB에 직전 테스트의 결과가 남을 수 있다.

이렇게 되면 다음 이전 테스트 때문에 다음 테스트가 실패할 가능성이 있다.

@AfterEach 를 사용하면 각 테스트가 종료될 때 마다 이 기능을 실행한다.

코드

MemoryMemeberRepositoryTest.java
@AfterEach
    public void aferEach(){
        repository.clearStore();
    }
    
// ...

MemoryMemberRepository.java
// 스토어를 비움
    public void clearStore(){
        store.clear();
    }
  • 여기서는 메모리 DB에 저장된 데이터를 삭제한다.
  • 테스트는 각각 독립적으로 실행되어야 한다.
  • 테스트 순서에 의존관계가 있는 것은 좋은 테스트가 아니다.

@Test

프로젝트를 진행하는 중간중간 구현이 잘 되어가고 있는지 확인하기 위해 테스트를 해주는게 중요하다.

Spring에서는 개발과 테스트를 분리해서 해볼 수 있다.

즉 src 영역과 test 영역이 나뉘어져 있다.

JUnit에서 테스트 할 대상을 표시한다.

@Test
    public void save(){
        Member member = new Member();
        member.setName("spring");

        repository.save(member);

        // 반환타입이 옵셔널이어서 옵셔널에서 꺼낼때는 뒤에 .get()붙여줌 test에서는...
        Member result = repository.findById(member.getId()).get();
        //System.out.println("result = " + (result == member));
        //기대한 값과 다르면 노란불이 뜸
        //Assertions.assertEquals(member, result);
        assertThat(member).isEqualTo(result);
    }

    @Test
    public void findByName(){
        //spring1, spring2 라는 회원의 회원가입이 된것임
        Member member1 = new Member();
        member1.setName("spring1");
        repository.save(member1);

        Member member2 = new Member();
        member2.setName("spring2");
        repository.save(member2);

        // "spring2라고 바꾸면 에러남 값이 다름"
        Member result = repository.findByName("spring1").get();

        assertThat(result).isEqualTo(member1);
    }

    @Test
    public void findAll(){
        Member member1 = new Member();
        member1.setName("spring1");
        repository.save(member1);

        Member member2 = new Member();
        member2.setName("spring2");
        repository.save(member2);

        List<Member> result = repository.findAll();

        // 기대했던 것은 2개니까 2가 아닌 다른 수가 들어가면 오류
        assertThat(result.size()).isEqualTo(2);
    }

참고
https://velog.io/@gillog/Spring-Annotation-%EC%A0%95%EB%A6%AC
https://melonicedlatte.com/2021/07/18/182600.html
https://cheershennah.tistory.com/179

profile
평생 소원이 누룽지

0개의 댓글