스프링부트

9장 CRUD와 SQL 쿼리 종합

hpehpeyy 2025. 1. 23. 18:54

9.1 JPA 로깅 설정하기

서버에서 데이터의 생성, 조회, 수정, 삭제 등을 요청하면 JPA의 리파지터리가 DB에 해당 요청을 전달합니다. 요청을 받은 DB는 자신의 언어, 즉 SQL로 쿼리를 작성해 테이블 속 데이터를 관리합니다. 여기서 쿼리(query)란 DB에 정보를 요청하는 구문을 말하는데, 데이터의 생성은 INSERT 문, 조회는 SELECT 문, 수정은 UPDATE 문, 삭제는 DELETET 문을 사용합니다.

CRUD 수행에 따른 SQL 쿼리 동작

 

이 장에서는 서버의 CRUD 수행에 따른 DB의 SQL 쿼리를 분석해 보겠습니다.

 

이를 위해 JPA 로깅 설정을 합니다. 로깅(logging)이란 시스템이 작동할 때 당시의 상태와 작동 정보를 기록하는 것을 말합니다. JPA의 로깅 설정은 resources > application.properties 파일에서 할 수 있습니다.

 

1. application.properties 파일을 엽니다. 코드를 보면 이전에 설정한 내용이 있습니다. 그 아래에 JAP 로깅을 설정해 보겠습니다. 먼저 로깅 레벨을 디버그(DEBUG)로 설정합니다.

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

 

로깅 레벨은 7단계가 있습니다. 출력 레벨을 설정하면 해당 레벨 이상의 로그가 출력됩니다. 예를 들어, INFO로 설정하면 그 이상의 레벨, 즉 INFO/WARN/ERROR/FATAL/OFF 레벨의 모든 로그가 기록됩니다. 여기에서는 JAP가 동작할 때 수행되는 SQL 쿼리를 보기 위해 로그 레벨을 DEBUG로 설정합니다.

레벨 1 TRACE DEBUG 레벨 보다 더 상세한 정보
레벨 2 DEBUG 응용 프로그램을 디버깅하는 데 필요한 세부 정보
레벨 3 INFO 응용 프로그램의 순조로운 진행 정보
레벨 4 WARN 잠재적으로 유해한 상황 정보
레벨 5 ERROR 응용 프로그램이 수행할 수 있는 정도의 오류 정보
레벨 6 FATAL 응용 프로그램이 중단될 만한 심각한 오류 정보
레벨 7 OFF 로깅 기능 해제

 

2. 서버를 실행해 정말로 SQL 쿼리가 로그로 찍히는지 확인합니다. 인텔리제이의 실행창을 보면 이전에는 보지 못한 org.hibernate.SQL 문구가 나온 것을 볼 수 있습니다. 해당 문구 옆으로 drop, create로 시작하는 SQL 쿼리를 확인할 수 있습니다.

그런데 쿼리들이 한 줄로 나와 한눈에 보기 힘듭니다. 보기 좋게 정렬해 보겠습니다.

 

3. application.properties 파일로 돌아와 format_sql을 true 하는 코드를 추가합니다. 이렇게 하면 한 줄로 작성된 SQL 쿼리에 줄바꿈을 적용할 수 있습니다.

#디버그 레벨로 쿼리 출력
logging.level.org.hibernate.SQL=DEBUG
#쿼리 줄바꿈하기
spring.jpa.properties.hibernate.format_sql=true

 

4. 서버를 재시작하고 실행창의 로그가 어떻게 변했는지 확인합니다. SQL 쿼리를 줄바꿈해 출력하는 것을 확인할 수 있습니다.

 

5. JPA 쿼리에서 DB로 넘어가는 매개변수 값을 확인하는 코드를 추가합니다. 종종 로그로 찍힌 SQL 쿼리를 보다 보면 ?(물음표)를 볼 수 있는데, 다음 코드를 추가하면 ?에 들어 있는 값을 볼 수 있습니다.

#매개변수 값 보여주기
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE

 

6. 서버를 재시작하고 문제없이 동작하는지 확인합니다.

 

7. 마지막으로, 그동안 H2 DB에 접속할 때 매번 JDBC URL을 새로 검색해서 입력했는데 이 URL을 고정하겠습니다. 먼저 유니크 URL 설정을 false로 하고, 다음으로 어떤 값을 고정 URL로 할 건지 적어 줍니다. 여기서는 jdbc:h2:mem:testdb라는 고정 URL을 사용하겠습니다.

#고정 URL 설정하기
spring.datasource.url=jdbc:h2:mem:testdb

 

8. 서버를 재시작하고 'jdbc'를 검색해 보면 설정한 값으로 바뀐 것을 확인할 수 있습니다.

 

9. loaclhost:8080/h2-console 페이지에 접속해 고정 URL을 입력합니다. [Connect] 버튼을 클릭해 보면 잘 접속됩니다.


9.2 SQL 쿼리 로그 확인하기

이제 서버에서 데이터를 생성, 조회, 수정, 삭제할 때 내부에서 어떤 SQL 쿼리가 동작하는지 로그를 통해 확인할 수 있습니다.

 

9.2.1 데이터 생성 시: INSERT 문

가장 먼저 데이터를 생성(Create)할 때 어떤 SQL 쿼리가 동작하는지 확인해 보겠습니다.

 

- id 자동 생성 전략 추가하기

localhost:8080/articles에 접속해 데이터를 하나 만들어 보겠습니다. New Article을 클릭하고 제목에 제목제목제목, 내용에 내용내용내용을 입력한 후 [Submit] 버튼을 클릭합니다. 그런데 에러 페이지가 뜹니다.

 

인텔리제이 실행창을 위쪽으로 스크롤해 보면 INSERT 문이 있고 그 아래에 SQL 에러 메시지와 함께 Unique index or primary key violation이라는 문구가 있습니다. 이는 '고유 인덱스 또는 기본키 위반'이라는 뜻입니다.

 

data.sql 파일을 열어 보면 3개의 더미 데이터를 생성하는 INSERT 문이 있습니다. 서버를 재시작할 때마다 더미 데이터를 생성하는 코드입니다. 여기서 1번, 2번 3번 데이터를 집어넣었는데, 새 게시글을 작성할 때 id를 1번 부터 넣게(Article.java에서 id 값을 자동으로 1씩 증가) 해서 에러가 난 것입니다. 즉, id가 중복된 거죠.

 

DB의 id 값은 중복되면 안 됩니다. 이렇게 테이블에 저장된 각 데이터를 유일하게 구분할 수 있도록 지정한 속성을 기본키(primary key)라고 합니다. article 테이블에서 기본키는 id입니다.

 

id가 중복되는 문제를 해결해 보겠습니다.

 

1. com.example.firstproject > entity > Article.java 파일을 엽니다. id 변수에 @Id와 @GeneratedValue 어노테이션이 있습니다. id를 자동 생성 하기 위해 붙인 건데, 이 코드를 수정해 DB가 알아서 id를 넣을 수 있도록 하겠습니다.

 

@GeneratedValue 어노테이션에서 괄호를 열고 strategy = GenerationType.IDENTITY를 입력한 후 괄호를 닫습니다. 이렇게 id 자동 생성 전략을 추가하면 앞으로 데이터를 생성할 때마다 DB가 알아서 id에 1, 2, 3, 4 등 값을 넣어 줍니다.

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY) //DB가 id 자동 생성
private Long id;

 

2. DB가 id를 자동 생성 하므로 data.sql에 입력값으로 준 id 속성과 속성 값 1, 2, 3을 삭제합니다.

INSERT INTO article(title, content) VALUES('가가가가', '1111');
INSERT INTO article(title, content) VALUES('나나나나', '2222');
INSERT INTO article(title, content) VALUES('다다다다', '3333');

 

- 데이터 생성 시 동작하는 SQL 로그 확인하기

id 중복 문제를 해결했으니 다시 데이터를 생성하겠습니다. 서버를 재시작하고 loaclhost:8080/articles 페이지에 접속합니다. New Article을 클릭해 제목에 제목제목제목, 내용에 내용내용내용을 입력한 후 [Submit] 버튼을 클릭합니다. id가 4인 데이터가 저장된 것을 확인할 수 있습니다.

 

이 과정에서 어떤 쿼리가 동작했는지 확인해 볼까요? 인텔리제이 실행창을 위로 스크롤해 보면 INSERT 문이 있습니다.

 - id 값에는 default가 들어 있습니다. DB가 알아서 id를 생성해 준다는 뜻입니다.

 - content에는 ?가 들어 있습니다. 아랫줄에 생성된 Article 객체를 보면 content에 있는 내용내용내용을 확인할 수 있습니다.

 - title에도 ?가 있습니다. 마찬가지로 Article 객체를 보면 title에 제목제목제목이 들어 있습니다.

 

이와 같이 데이터를 생성할 때는 INSERT 문이 동작합니다.

 

9.2.2 데이터 조회 시: SELECT 문

데이터를 조회(Read)할 때는 어떤 SQL 쿼리가 동작하는지 확인해 보겠습니다.

 

1. localhost:8080/articles 페이지에 접속합니다. 모든 게시글을 가져와 보여 주는데, 이때 수행되는 쿼리를 확인해 봅시다.

인텔리제이 실행창을 보면 데이터를 조회할 때 SELECT 문이 수행된 것을 볼 수 있습니다. 재명명 연산(.)이 사용돼 복잡해 보이지만, 여기서는 필요한 부분만 살펴보겠습니다.

 

SELECT 절에는 조회할 속성인 id, content, title이 있고 FROM 절에는 조회할 테이블명인 article이 있습니다. 이는 article 테이블에서 id, content, title을 조회하라는 쿼리입니다. 이 쿼리가 수행돼 목록 페이지에서 모든 게시글을 볼 수 있습니다.

 

2. 4번 데이터 하나만 조회했을 때는 어떤 쿼리가 동작할까요? 목록 페이지에서 4번을 클릭해봅시다.

인텔리제이 실행창을 보면 똑같이 SELECT 문이 있는데 WHERE 절이 추가됐습니다. WHERE 절에는 id가 ?인 데이터를 가져오라고 되어 있습니다. ?에 들어가는 값은 로그에서 id 값을 보면 알 수 있습니다. 바로 4입니다. 즉, id가 4번인 데이터를 조회하라는 쿼리가 수행된 것입니다.

 

9.2.3 데이터 수정 시: UPDATE 문

데이터를 수정(Update)할 때는 어떤 SQL 쿼리가 동작하는지 확인해 보겠습니다.

localhost:8080/articles/4 페이지에서 [Edit] 버튼을 클릭합니다. 수정 폼이 나타나면 제목은 라라라라, 내용은 4444로 수정하고 [Submit] 버튼을 클릭합니다.

 

인텔리제이 실행창을 보면 UPDATE 쿼리가 수행됐습니다. ?는 JPA에서 DB로 전달하는 매개변수라고 했습니다. ?에 들어갈 값을 로그에서 찾아보면 content는 4444, title은 라라라라, id는 4인 것을 알 수 있습니다.

 

9.2.4 데이터 삭제 시: DELETE 문

마지막으로 데이터를 삭제(Delete)할 때는 어떤 SQL 쿼리가 동작하는지 확인해 보겠습니다.

localhost:8080/articles/4 페이지에서 [Delete] 버튼을 클릭합니다. 어떤 쿼리를 수행했을까요?

 

인텔리제이 실행창을 보면 DELETE 쿼리를 수행했습니다. WHERE 절을 보면 id가 ?인 데이터를 지우라고 되어 있습니다. ?의 값을 확인해 보면 4입니다. 이 쿼리는 article 테이블에서 id가 4인 데이터를 삭제합니다.

 

웹 페이지 결과를 보면 4번 데이터가 삭제된 것을 확인할 수 있습니다.

지금까지 쿼리를 로그로 찍어 보며 서버에서 데이터를 생성, 조회, 수정, 삭제했을 때 내부에서 어떤 동작이 일어나는지 확인했습니다.


9.3 기본 SQL 쿼리 작성하기

이번에는 새로운 테이블 coffee를 만들어 SQL 쿼리를 작성하는 예제를 실습하겠습니다. localhost:8080/h2-console 페이지에 접속합니다. 앞서 설정한 고정 URL값으로 설정됐는지 확인하고 [Connect] 버튼을 클릭합니다.

 

9.3.1 coffee 테이블 만들기

인텔리제이 실행창으로 가서 CREATE를 검색합니다. CREATE TABLE 문이 수행된 것을 볼 수 있습니다. CREATE TABLE 문은 테이블을 만드는 쿼리로, 다음 쿼리는 id, content, title이라는 속성을 가진 article이라는 이름의 테이블을 만들라는 뜻입니다.

 

그런데 이 쿼리는 어떻게 동작한 걸까요? 앞에서 만든 Article 클래스를 보면 알 수 있습니다. entity > Article.java 파일을 열어 봅시다. 코드 윗부분을 보면 @Entity가 있죠? 이 어노테이션은 DB가 해당 객체를 인식하도록 붙인 것입니다. 더 풀어서 설명하면 해당 클래스로 테이블을 만들라는 뜻입니다. 테이블의 속성으로는 그 아래를 보면 알 수 있듯이 id, title, content가 선언돼 있습니다.

@Entity //1.엔티티 선언
@Getter

public class Article {
    @Id //3.엔티티의 대푯값 지정
    @GeneratedValue(strategy = GenerationType.IDENTITY) //DB가 id 자동 생성
    private Long id;

    @Column //2.title 필드 선언, DB 테이블의 title 열과 연결됨
    private String title;

    @Column //2.content 필드 선언, DB 테이블의 content 열과 연결됨
    private String content;

 

이와 같이 서버를 실행하면 새로운 Article 객체가 만들어지기 때문에 CREATE TABLE 문도 자동으로 수행됩니다.

 

CREATE TABLE 문의 형식은 다음과 같습니다.

CREATE TABLE 테이블명(
    속성명1 자료형,
    속성명2 자료형,
    속성명3 자료형,
    PRIMARY KEY (기본키)
);

 

1. 인텔리제이 실행창에서 확인한 CREATE TABLE 문을 복사한 후 DB 편집기에 붙여 넣습니다. 테이블의 이름은 coffee로 수정하고 속성 중에 id는 그대로 둔 채 나머지를 name과 price로 수정합니다. 그리고 price의 자료형을 integer(정수)로 수정합니다.

 

2. [Run]을 클릭하면 coffee 테이블이 만들어집니다. 속성을 보면 id, name, price가 있습니다.

 

3. [Clear]를 클릭해 편집기에 내용을 싹 지운 후 coffee 테이블을 선택하고 [Run]을 클릭합니다. 아직 데이터가 아무것도 없습니다. 여기에 새 데이터를 넣어 보겠습니다.

 

9.3.2 coffee 데이터 생성하기

테이블에 데이터를 넣어 보겠습니다. 데이터를 생성할 때는 INSERT 문을 사용합니다.

 

1. 인텔리제이 실행창에서 INSERT를 검색한 후 INSERT 문 전체를 복사해 DB 편집기에 붙여 넣습니다. INSERT INTO 절의 테이블명과 속성명, 그리고 VALUES 절에 추가할 데이터를 다음과 같이 수정한 후 [Run]을 클릭합니다.

INSERT
INTO
    coffee
    (id, name, price)
VALUES
    (1, '아메리카노', 4100);

 

2. 데이터가 잘 들어갔는지 확인하기 위해 [Clear]를 클릭해 편집기의 내용을 싹 지운 후 coffee 테이블을 선택하고 [Run]을 클릭합니다. 결과를 보면 1번 아메리카노가 잘 들어갔습니다.

 

3. 이번에는 여러 데이터를 한꺼번에 생성해 보겠습니다. INSERT INTO 절은 기존 쿼리 그대로 두고 VALUES 절에 새로 생성할 데이터를 쉼표(,)로 구분해 추가합니다. 다시 [Run]을 클릭합니다.

INSERT
INTO
    coffee
    (id, name, price)
VALUES
    (2, '라떼', 4600),
    (3, '모카', 5100),
    (4, '오늘의 커피', 3800);

 

4. 데이터가 잘 들어갔는지 확인하기 위해 [Clear]를 클릭해 편집기의 내용을 싹 지운 후 coffee 테이블을 선택하고 [Run]을 클릭합니다. 결과를 보면 추가한 데이터 3개가 잘 들어갔습니다. 

 

9.3.3 coffee 데이터 조회하기

테이블에 생성한 데이터를 조건을 걸어 조회해 보겠습니다. 테이블에서 데이터를 조회할 때는 SELECT 문을 사용합니다.

 

1. 인텔리제이 실행창에서 SELECT를 검색해 SELECT 문 전체를 복사한 후 DB 편집기에 붙여 넣습니다. id가 3번인 데이터를 조회해 볼까요? SELECT 절에 조회할 속성은 id, name, price로 FROM 절에 조회할 테이블명은 coffee로, WHERE 절에 조건은 id = 3으로 수정합니다.

SELECT
    id, name, price
FROM
    coffee
WHERE
    id = 3;

 

2. [Run]을 클릭해 보면 3번에 저장된 모카가 잘 출력됩니다.

 

3. WHERE 절을 빼고 조회하면 coffee 테이블의 모든 데이터를 출력합니다.

SELECT
    id, name, price   
FROM
    coffee;

 

9.3.4 coffee 데이터 수정하기

테이블에 저장한 데이터를 수정해 보겠습니다. 데이터를 수정할 때는 UPDATE 문을 사용합니다.

 

1. 인텔리제이 실행창에서 UPDATE를 검색해 UPDATE 문 전체를 복사한 후 붙여 넣습니다. id가 4번인 오늘의 커피 가격을 9,900원으로 수정해 볼까요? UPDATE 절의 테이블명은 coffee, SET 절에 수행할 내용은 price = 9900, WHERE 절에 조건은 id = 4로 바꾸고 [Run]을 클릭합니다.

UPDATE
    coffee
SET
    price = 9900
WHERE
    id = 4;

 

2. [Clear]를 클릭해 편집기의 내용을 싹 지운 후 coffee 테이블을 선택하고 [Run]을 클릭합니다. 오늘의 커피 가격이 9,900원으로 수정된 것을 확인할 수 있습니다.

 

9.3.5 coffee 데이터 삭제하기

마지막으로 테이블에서 데이터를 삭제해 보겠습니다. 데이터를 삭제할 때는 DELETE 문을 사용합니다.

 

1. id가 4번인 오늘의 커피를 지워 볼까요? 인텔리제이에서 DELETE를 검색해 DELETE 문 전체를 복사해 붙여 넣고 FROM 절의 테이블명은 coffee, WHERE 절에 조건은 id = 4로 수정하고 [Run]을 클릭합니다.

DELETE
FROM
    coffee
WHERE
    id = 4;

 

2. [Clear]를 클릭해 편집기의 내용을 싹 지운 후 coffee 테이블을 선택하고 [Run]을 클릭합니다. coffee 테이블에서 id가 4번인 오늘의 커피가 삭제된 것을 확인할 수 있습니다.

 

여기까지 간단하게 CRUD와 관련한 SQL 쿼리를 로그로 찍고 다시 한번 DB에서 직접 쿼리를 작성하는 예제를 연습해 봤습니다.

'스프링부트' 카테고리의 다른 글

11장 HTTP와 REST 컨트롤러  (1) 2025.02.07
10장 REST API와 JSON  (3) 2025.02.04
8장 게시글 삭제하기: Delete  (0) 2025.01.23
7장 게시글 수정하기: Update  (0) 2025.01.22
6장 게시판 내 페이지 이동하기  (2) 2025.01.20