포도가게의 개발일지
[Spring] CRUD 본문
DTO(data transfer object) : form data를 주고 받는 객체
form tag로 보내는건 query param이라 json type인 body를 쓰지 않는다.
JPA
Create
@Controller
public class ArticleController {
@Autowired
private ArticleRepository articleRepository;
@GetMapping("/articles/new")
public String newArticleForm(){
return "articles/new";
}
@PostMapping("/articles/create")
public void createArticleForm(ArticleForm form){
Article article = form.toEntity();
System.out.println(article.toString());
Article saved = articleRepository.save(article);
}
}
Read
@Controller
@AllArgsConstructor
public class ArticleController {
private ArticleRepository articleRepository;
public String show(@PathVariable Long id, Model model){
Article articleEntity = articleRepository.findById(id).orElse(null);
log.info(articleEntity.toString());
model.addAttribute("article", articleEntity);
return "articles/show";
}
}
1) Query string
/articles?id=123 # Fetch a user who has id of 123
위에서 보는 것처럼 ? 뒤에 id란 변수에 값을 담아 백엔드에 전달하는 방식이 Query string이다. users에 담긴 정보 중 id 123번의 자료를 달라는 요청이다.
2) Path Variable
/articles/123 # Fetch a user who has id 123
위와 동일한 요청을 경로를 지정하여 요청할 수도 있는데 이것을 Path Variable이라고 한다.
3) Query string과 Path variable은 각각 언제 쓰면 좋은가?
일반적으로 우리가 어떤 자원(데이터)의 위치를 특정해서 보여줘야 할 경우 Path variable을 쓰고, 정렬하거나 필터해서 보여줘야 할 경우에 Query parameter를 쓴다. 아래가 바로 그렇게 적용한 사례이다.
/articles # Fetch a list of articles
/articles?occupation=programer # Fetch a list of programer article
/articles/123 # Fetch a article who has id 123
jpa entity 기본 생성자(Default Constructor)
- JPA는 DB 값을 객체 필드에 주입할 때 기본 생성자로 객체를 생성한 후 이러한 Reflection을 사용하여 값을 매핑하기 때문이다.
- 정확히 이야기하면 Entity는 반드시 파라미터가 없는 생성자가(default constructor) 있어야 하고, 이것은 public 또는 protected 이어야 한다.
class Student{
# 파라미터가 없는 생성자
public Student(){}
}
Java Reflection 이란?
구체적인 클래스 타입을 알지 못해도, 그 클래스의 메소드, 타입, 변수들에 접근할 수 있도록 해주는 자바 API
컴파일 시간(Compile Time)이 아니라 실행 시간(Run Time)에 동적으로 특정 클래스의 정보를 객체화를 통해 분석 및 추출해낼 수 있는 프로그래밍 기법이라고 표현할 수 있다.
why?
- 실행 시간(Run Time)에 동적으로 특정 클래스의 정보를 객체화를 통해 분석 및 추출해낼 수 있는 프로그래밍 기법이라고 표현할 수 있다.
- 프레임워크나 라이브러리는 사용자가 어떤 클래스를 만들지 예측할 수 없기 때문에 동적으로 해결해주기 위해 Reflection을 사용한다.
How?
- 자바에서는 JVM이 실행되면 사용자가 작성한 자바 코드가 컴파일러를 거쳐 바이트 코드로 변환되어 static 영역에 저장된다.
- 클래스 이름만 알고 있다면 언제든 static 영역을 뒤져서 정보를 가져올 수 있는 것이다.
public static void main(String[] args) {
Object obj = new Car("foo", 0);
obj.move(); // 컴파일 에러 발생 java: cannot find symbol
}
- 자바는 컴파일러를 사용한다. 즉 컴파일 타임에 타입이 결정된다. obj라는 이름의 객체는 컴파일 타임에 Object로 타입이 결정됐기 때문에 Object 클래스의 인스턴스 변수와 메서드만 사용할 수 있다.
public static void main(String[] args) throws Exception {
Object obj = new Car("foo", 0);
Class carClass = Car.class;
Method move = carClass.getMethod("move");
// move 메서드 실행, invoke(메서드를 실행시킬 객체, 해당 메서드에 넘길 인자)
move.invoke(obj, null);
Method getPosition = carClass.getMethod("getPosition");
int position = (int)getPosition.invoke(obj, null);
System.out.println(position);
// 출력 결과: 1
}
Class Main{
public static void main(String[] args){
Class Article = Class.forName("ArticleForm");
Method toEntity = Article.getMethod("toEntity");
toEntity.invoke();
}
}
어디에 쓰는걸까?
- 실제로 우리가 코드를 작성할 때는 예제와 같이 작성하지 않는다. 그러므로 우리가 코드를 작성하면서 Reflection을 활용할 일은 거의 없다. 구체적인 클래스를 모를 일이 거의 없기 때문이다.
- 보통 intellij의 자동완성, jackson 라이브러리, Hibernate 등등 많은 프레임워크나 라이브러리에서 Reflection을 사용하고 있다.
- Spring Data JPA 에서 Entity에 기본 생성자가 필요한 이유도 동적으로 객체 생성 시 Reflection API를 활용하기 때문이다. Reflection API로 가져올 수 없는 정보 중 하나가 생성자의 인자 정보이다.
Update, Delete
package com.example.firstproject.api;
import java.util.*;
import com.example.firstproject.dto.ArticleForm;
import com.example.firstproject.entity.Article;
import com.example.firstproject.repository.ArticleRepository;
import lombok.AllArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@RestController
@AllArgsConstructor
public class ArticleApiController {
private ArticleRepository articleRepository;
// GET
@GetMapping("/api/articles")
public ResponseEntity<List<Article>> getArticleList(){
return ResponseEntity.status(HttpStatus.OK).body(articleRepository.findAll());
}
@GetMapping("/api/articles/{id}")
public ResponseEntity<Article> getArticle(@PathVariable Long id){
Article article = articleRepository.findById(id).orElse(null);
if(article == null){
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null);
}
return ResponseEntity.status(HttpStatus.OK).body(article);
}
@PostMapping("/api/article")
public ResponseEntity<Article> postArticle(@RequestBody ArticleForm articleForm){
Article article = articleForm.toEntity();
return ResponseEntity.status(HttpStatus.CREATED).body(articleRepository.save(article));
}
@PatchMapping("/api/article")
public ResponseEntity<Article> patchArticle(@RequestBody ArticleForm articleForm){
Article target = articleRepository.findById(articleForm.toEntity().getId()).orElse(null);
if(target == null){
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null);
}
target.patch(articleForm.toEntity());
return ResponseEntity.status(HttpStatus.OK).body(articleRepository.save(target));
}
@DeleteMapping("/api/article/{id}")
public ResponseEntity<Article> deleteArticle(@PathVariable Long id){
Article target = articleRepository.findById(id).orElse(null);
if(target == null){
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null);
}
articleRepository.deleteById(id);
return ResponseEntity.status(HttpStatus.OK).body(null);
}
}
주소설계
@RestController vs @Controller
1. @RestController란?
- 스프링프레임워크 4.x 버전 이상부터 사용가능한 어노테이션으로 @Controller에 @ResponseBody가 결합된 어노테이션입니다. 컨트롤러 클래스에 @RestController를 붙이면, 컨트롤러 클래스 하위 메서드에 @ResponseBody 어노테이션을 붙이지 않아도 문자열과 JSON 등을 전송할 수 있습니다.
// 동일하게 동작합니다
@Controller
@ResponseBody
public class MVCController{
logic...
}
@RestController
public class ReftFulController{
logic...
}
ResponseEntity
'Spring boot' 카테고리의 다른 글
[Spring] 생성자주입? 필드주입? 세터주입? (0) | 2022.01.05 |
---|---|
[Java] Java 원시타입 vs 참조타입 (0) | 2022.01.05 |
[Spring] MVC 패턴 (0) | 2022.01.04 |
spring boot 시작하기 (0) | 2022.01.03 |
Spring boot? (0) | 2022.01.03 |