ECC/스프링 부트

[스프링 부트] 9장~11장

jiheechoi 2025. 6. 28. 06:05

9. CURD와 SQL 쿼리 종합

1.  JPA 로깅 설정하기

-서버에서 데이터 생성, 조회, 수정, 삭제 등을 요청하면 JPA의 리파지터리가 DB에 해당 요청을 전달함. → 요청을 받은 DB는 SQL로 쿼리를 작성해 테이블 속 데이터를 관리.

-쿼리란, DB에 정보를 요청하는 구문으로 INSERT문, SELECT문, UPDATE문, DELETE문이 있음.

*JPA 로깅 레벨 설정하기(application.properties 수정)

#JPA 로깅 설정
#디버그 레벨로 쿼리 출력
logging.level.org.hibernate.SQL=DEBUG

 

→ 로깅 레벨에는 7단계가 있고, 레벨을 설정하면 해당 레벨 이상의 로그가 출력됨. DEBUG로 설정하면 JPA가 동작할 때 수행되는 SQL 쿼리를 볼 수 있음.

*그외 수정사항(application.properties에 추가)

#JPA 로깅 설정
#디버그 레벨로 쿼리 출력
logging.level.org.hibernate.SQL=DEBUG
#쿼리 줄바꿈하기
spring.jpa.properties.hibernate.format_sql=true
#매개변수 값 보여주기
logging.level.org.hibernatetype.descriptor.sql.BasicBinder=TRACE

#DB URL 설정
#유니크 URL 생성하지 않기
spring.datasource.generate-unique-name=false
#고정 URL 설정하기
spring.datasource.url=jdbc:h2:mem:testdb

2.  SQL 쿼리 로그 확인하기

1) 데이터 생성시:INSERT문

-데이터 생성하기

 

-데이터 생성시 INSERT문 동작

 

2) 데이터 조회 시: SELECT문

-localhost:8080/articles 페이지 접속해서 모든 데이터 조회하기

-데이터 조회시 SELECT문 동작

 

3) 데이터 수정시: UPDATE문

-id=4인 article을 수정 

-데이터 수정시 UPDATE문 동작

 

4) 데이터 삭제 시: DELETE문

-id=4인 article 삭제하기

-데이터 삭제시 DELETE문 동작

 

3. 기본 SQL 쿼리 작성하기

1) coffee 테이블 만들기

 

2) coffee 데이터 생성하기

 

3) coffee 데이터 조회하기

 

4) coffee 데이터 수정하기

 

5) coffee 데이터 삭제하기 

10. REST API와 JSON

1. REST API와 JSON의 등장배경

-REST API는 서버의 자원을 클라이언트에 구애받지 않고 사용할 수 있게 하는 설계 방식임. 

-REST API 방식에서는 HTTP 요청에 대한 응답으로 서버의 자원을 반환함. → 서버에서 보내는 응답이 특정 기기에 종속되지 않도록 모든기기에서 통용될 수 있는 응답 데이터를 반환(JSON)

-JSON 데이터: key와 value로 구성된 정렬되지 않은 속성의 집합. 키는 문자열이므로 항상 큰따옴표로 감싸고, 값은 문자열인 경우에만 큰따옴표로 감쌈.

 

2. REST API 동작 살펴보기

1) GET 요청하고 응답받기

-JSON placeholder 사이트에서 확인한 게시글, 댓글 등의 자원을 Talend API Tester 프로그램을 이용해 JSON 형식으로 받아오는 실습

모든 게시글 조회하기
1번 게시글 조회

※ HTTP 상태 코드: 클라이언트가 보낸 요청이 성공했는지, 실패했는지 알려 주는 코드

  • 1XX(정보): 요청이 수신돼 처리 중임.
  • 2XX(성공): 요청이 정상적으로 처리됨.
  • 3XX(리다이렉션 메시지): 요청을 완료하려면 추가 행동이 필요함.
  • 4XX(클라이언트 요청 오류): 클라이언트의 요청이 잘못돼 서버가 요청을 수행할 수 없음.
  • 5XX(서버 응답 오류): 서버 내부에 에러가 발생해 클라이언트 요청에 대해 적절히 수행하지 못함.

※ HTTP 요청 메시지와 응답 메시지

-HTTP 메서드를 통한 데이터 조회 요청과 그에 대한 응답은 HTTP 메시지에 실려 전송됨. 요청할 때는 HTTP 요청 메시지에, 응답할 때는 응답 메시지에 내용이 실림.

-HTTP 메시지의 구성

  • 시작 라인: HTTP 요청 또는 응답 내용이 있음. 시작라인은 항상 한줄로 끝남.
  • 헤더: HTTP 전송에 필요한 부가 정보가 있음.
  • 빈 라인: 헤더의 끝을 알리는 빈 줄로, 헤더가 모두 전송되었음을 알림.
  • 본문: 실제 전송하는 데이터가 있음.

2) POST 요청하고 응답받기

데이터 생성 요청

 

3) PATCH 요청하고 응답받기

1번 게시글 수정 요청하기

 

4) DELETE 요청하고 응답받기

10번 게시글 삭제 요청하기

 

11. HTTP와 REST 컨트롤러

1. REST API의 동작 이해하기

-REST API 방식에서는 HTTP 요청에 대한 응답으로 서버의 자원을 반환함. → 서버에서 보내는 응답이 특정 기기에 종속되지 않도록 모든기기에서 통용될 수 있는 응답 데이터를 반환.

-REST API의 응답 표준으로 사용하는 것이 JSON.

-REST API를 잘 구현하면 클라이언트가 기기에 구애받지 않고 서버의 자원을 이용할 수 있으며, 서버가 클라이언트의 요청에 체계적으로 대응 가능함. 

 

2. REST API의 구현 과정

-주소 설계

  • 조회 요청: /api/articles or /api/articles/{id} → GET 메서드로 Article 목록 전체 또는 단일 Article 조회
  • 생성 요청: /api/articles → POST 메서드로 새로운 Article을 생성해 목록에 저장
  • 수정 요청: /api/articles/{id} → PATCH 메서드로 특정 Article의 내용을 수정
  • 삭제 요청: /api/articles/{id} → DELETE 메서드로 특정 Article 삭제

- URL 요청을 받아 그 결과를 JSON으로 반환해 줄 컨트롤러 생성(REST 컨트롤러)

→ 응답할 때 적절한 상태의 코드를 반환하기 위해 ResponseEntity 클래스 활용

 

3. REST API 구현하기

1) REST 컨트롤러 생성

-FirstApiController.java 생성

package com.example.firstproject.api;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class FirstApiController {
    @GetMapping("/api/hello")
    public String hello() {
        return "Hello World";
    }
}

 

-REST 컨트롤러와 일반 컨트롤러의 차이: REST 컨트롤러는 JSON이나 텍스트 같은 데이터를 반환하는 반면, 일반 컨트롤러는 뷰 페이지를 반환함. 

 

2) REST API: GET/POST/PATCH/DELETE 구현하기

-FirstApiController.java

package com.example.firstproject.api;

import com.example.firstproject.dto.ArticleForm;
import com.example.firstproject.entity.Article;
import com.example.firstproject.repository.ArticleRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@Slf4j
@RestController
public class ArticleApiController { 
    @Autowired
    private ArticleRepository articleRepository;
    
    //GET
    @GetMapping("/api/articles")
    public List<Article> index() {
        return articleRepository.findAll();
    }

    @GetMapping("/api/articles/{id}")
    public Article show(@PathVariable Long id) {
        return articleRepository.findById(id).orElse(null);
    }
    
    //POST
    @PostMapping("/api/articles")
    public Article create(@RequestBody ArticleForm dto) {
        Article article = dto.toEntity();
        return articleRepository.save(article);
    }
    
    //PATCH
    @PatchMapping("/api/articlces/{id}")
    public ResponseEntity<Article> update(@PathVariable Long id, @RequestBody ArticleForm dto) {
        Article article = dto.toEntity();
        log.info("id: {}, article: {}", id, article.toString());
        Article target = articleRepository.findById(id).orElse(null);
        if (target == null || id != article.getId()) {
            log.info("잘못된 요청! id: {}, article: {}", id, article.toString());
            return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null);
        }
        target.patch(article);
        Article updated = articleRepository.save(article);
        return ResponseEntity.status(HttpStatus.OK).body(updated);
    }
    
    //DELETE
    @DeleteMapping("/api/articles/{id}")
    public ResponseEntity<Article> delete(@PathVariable Long id) {
        Article target = articleRepository.findById(id).orElse(null);
        if (target == null) {
            return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null);
        }
        articleRepository.delete(target);
        return ResponseEntity.status(HttpStatus.OK).build();
    }
}

 

 

-Article.java 수정

package com.example.firstproject.entity;

import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;

@AllArgsConstructor
@NoArgsConstructor //기본 생성자 추가 어노테이션
@ToString
@Entity
@Getter
public class Article {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY) //DB가 id 자동 생성
    private Long id;
    @Column
    private String title;
    @Column
    private String content;

    public void patch(Article article) {
        if (article.title != null) {
            this.title = article.title;
        }
        if (article.content != null) {
            this.content = article.content;
        }
    }
}