728x90

일단 바로 만들어보고 시작하자.

 

intellij에서 JDK17로 Maven 프로젝트를 생성한다.

그리고 pom.xml을 다음과 같이 작성한다.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>jpa-basic</groupId>
    <artifactId>JPA-01</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <hibernate.verstion>6.0.0.FINAL</hibernate.verstion>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>${hibernate.verstion}</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-hikaricp</artifactId>
            <version>${hibernate.verstion}</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.28</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.11</version>
        </dependency>
    </dependencies>

</project>

우선 이렇게 작성해주고 src/main/resources/META-INF에 가서는 persistence.xml을 만든 후

<?xml version="1.0" encoding="utf-8" ?>

<persistence xmlns="https://jakarta.ee/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="https://jakarta.ee/xml/ns/persistence
                https://jakarta.ee/xml/ns/persistence/persistence_3_0.xsd"
             version="3.0">

    <persistence-unit name="jpabegin" transaction-type="RESOURCE_LOCAL">
        <class>jpabasic.reserve.domain.User</class>
        <exclude-unlisted-classes>true</exclude-unlisted-classes>
        <properties>
            <property name="jakarta.persistence.jdbc.driver" value="com.mysql.cj.jdbc.Driver"/>
            <property name="jakarta.persistence.jdbc.url"
                      value="jdbc:mysql://localhost/jpabegin?characterEncoding=utf8"/>
            <property name="jakarta.persistence.jdbc.user" value="jpauser"/>
            <property name="jakarta.persistence.jdbc.password" value="12345678"/>

            <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
            <property name="hibernate.hikari.poolName" value="pool"/>
            <property name="hibernate.hikari.maximumPoolSize" value="10"/>
            <property name="hibernate.hikari.minimumIdle" value="10"/>
            <property name="hibernate.hikari.connectionTimeout" value="1000"/>
        </properties>
    </persistence-unit>
</persistence>

이렇게 작성을 해준다.

MySQL을 사용하고 사용자 정보를 저렇게 사용하기 때문에 저렇게 만든 것이다.

 

이 상태로 바로 class를 작성하러 가보자.

JDBC를 공부했으니 어느정도는 이해가 될 수도 있다.

package jpabasic.reserve.domain;

import jakarta.persistence.*;
import java.time.LocalDateTime;

@Entity
@Table(name = "user")
public class User {
    @Id
    private String email;
    private String name;
    private String create_date;

    public User(String email, String name, String create_date) {
        this.email = email;
        this.name = name;
        this.create_date = create_date;
    }

    protected User() {
        
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getCreate_date() {
        return create_date;
    }

    public void setCreate_date(String create_date) {
        this.create_date = create_date;
    }
}

클래스 위에 @Entity annotation으로 요소임을 알리고, @Table로 테이블을 지정하며, @Id로 식별자들을 지정해준다.

그리고 당연히 생성자와 Getter, Setter를 만들어준다.

 

import jakarta.persistence.*;
import jpabasic.reserve.domain.User;
import java.time.LocalDateTime;

public class UserSaveMain {
    public static void main(String[] args) {
        EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("jpabegin");
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        EntityTransaction transaction = entityManager.getTransaction();

        try{
            transaction.begin();
            User user = new User("hello@gmail.com", "hello", LocalDateTime.now());
            entityManager.persist(user);
            transaction.commit();
        } catch(Exception exception){
            exception.printStackTrace();
            transaction.rollback();
        }finally {
            entityManager.close();
        }

        entityManager.close();
    }
}

 

우선 EntityManagerFactory를 생성한다.

이 때 xml에서 작성했던 jpabegin 이름을 이용한다.

 

persist를 사용하면 parameter로 넘긴 객체가 DB에 저장이 된다.

실행을 해보면 잘 들어가는 것을 볼 수 있다.

당연히 한 번 더 추가하면 pk가 겹치기 때문에 추가할 수 없다고 나온다.

 

이번엔 조회다.

import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.EntityTransaction;
import jakarta.persistence.Persistence;
import jpabasic.reserve.domain.User;

public class UserGetMain {
    public static void main(String[] args) {

        String find_id = "hello@gmail.com";

        EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("jpabegin");

        EntityManager entityManager = entityManagerFactory.createEntityManager();
        EntityTransaction transaction = entityManager.getTransaction();

        try{
            transaction.begin();
            User user = entityManager.find(User.class, find_id);
            if (user == null) System.out.println("User 없음");
            else {
                System.out.printf("email : %s, name = %s, createDate = %s\n", user.getEmail(), user.getName(), user.getCreate_date());
            }
            transaction.commit();
        }catch (Exception exception){
            exception.printStackTrace();
            transaction.rollback();
        }finally {
            entityManager.close();
        }
        entityManager.close();
    }
}

entityManager에서 find로 찾아온다.

parameter는 해당 클래스와 PK이다.

실행을 해보면 정상적으로 조회되는 것을 볼 수 있다.

 

이번엔 수정이다.

import jakarta.persistence.*;
import jpabasic.reserve.domain.User;
import java.time.LocalDateTime;

public class UserUpdateMain {
    public static void main(String[] args) {
        EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("jpabegin");
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        EntityTransaction transaction = entityManager.getTransaction();

        try{
            transaction.begin();
            User user = entityManager.find(User.class, "hello@gmail.com");
            if(user == null) System.out.println("User 없음");
            else{
                user.setName("hi");
            }
            transaction.commit();
        } catch(Exception exception){
            exception.printStackTrace();
            transaction.rollback();
        }finally {
            entityManager.close();
        }

        entityManager.close();
    }
}

수정은 조회를 한 후 해당 클래스의 데이터를 변경하고 commit을 수행하면 된다.

transaction 범위 내에서 수정해야 한다.

이렇게 정상적으로 수행되는 것을 볼 수 있다.

'백엔드 > JPA' 카테고리의 다른 글

JPA 6장 (@Embeddable)  (0) 2023.03.17
JPA 5장 (Entity 식별자 생성 방식)  (0) 2023.03.16
JPA 4장 (Entity에 대하여)  (0) 2023.03.16
JPA 3장 (간단한 CRUD 구현해보기)  (0) 2023.03.16
JPA 2장 (영속 컨텍스트)  (0) 2023.03.15
728x90

이제 JDBC를 이용해서 데이터를 다뤄보자.

 

  • 데이터 수정하기

우선 MySQL로 가보자.

NOTICE 테이블에서

ID와 REGDATE, HIT는 사용자가 입력하는 정보는 아니다.

REGDATE는 현재 시각을 넣고, HIT는 조회할 때마다 하나씩 증가하며 ID는 1부터 시작해서 하나씩 증가할 것이다.

그렇기 때문에 일단 ID에 자동 증가를 설정해두고, HIT에 디폴트를 0으로 설정해준다.

 

그 후에 테스트로

INSERT INTO NOTICE(
    TITLE,
    WRITER_ID,
    CONTENT,
    REGDATE,
    FILES
) VALUES (
	"SEUNGKYU",
    "SK",
    "AAA",
    NOW(),
    ""
);

해당 값을 넣은 후 데이터가 잘 들어가는 지 테스트 해본다.

이렇게 잘 들어간다.

auto_increment를 다시 1로 설정해주고 JDBC로 넘어간다.

 

Program1의 코드를 그대로 복사해서 Program2를 만들었다.

기본적인 내용들은 크게 변하지는 statement를 PreparedStatement, executeQuery를 executeUpdate로 변경해주어야 한다.

해당 코드들은 조회에서만 사용하는 코드들이다.

 

executeUpdate는 조회가 아니가 때문에 결과 집합을 반환하지 않고, 업데이트 된 데이터의 개수를 반환한다.

 

우선 sql 문장부터 만들어 본다면

import java.sql.*;

public class Program2 {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {

        String driver = "com.mysql.cj.jdbc.Driver";
        String url = "jdbc:mysql://localhost:3306/jdbc";
        String user = "root";
        String passwd = "12345678";

        String title = "seungkyubook";
        String writer_id = "sk";
        String content = "aaa";
        String files = "";

        String sql = "INSERT INTO NOTICE (TITLE, WRITER_ID, CONTENT, REGDATE, FILES) VALUES ()";



        Class.forName(driver);

        Connection connection = DriverManager.getConnection(url, user, passwd);

        Statement statement = connection.createStatement();

        int result = statement.executeUpdate(sql);
        
        
        statement.close();
        connection.close();
    }
}

 

저렇게 만든 후 VALUES 안에 우리가 넣고 싶어하느 데이터들을 +로 연결해주어야 했다.

이렇게 이어 붙이기 위해서는 문자열에는 '를 또 달아주기도 해야한다.

너무 귀찮고 실수도 많이 일어나는 부분이다.

 

다행이게도 JDBC에서 이 부분을 도와준다.

방법은

import java.sql.*;

public class Program2 {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {

        String driver = "com.mysql.cj.jdbc.Driver";
        String url = "jdbc:mysql://localhost:3306/jdbc";
        String user = "root";
        String passwd = "12345678";

        String title = "seungkyubook";
        String writer_id = "sk";
        String content = "aaa";
        String files = "";

        String sql = "INSERT INTO NOTICE (TITLE, WRITER_ID, CONTENT, REGDATE, FILES) VALUES (?, ?, ?, NOW(),?)";

        Class.forName(driver);

        Connection connection = DriverManager.getConnection(url, user, passwd);

        PreparedStatement preparedStatement = connection.prepareStatement(sql);

        preparedStatement.close();
        connection.close();
    }
}

이렇게 문자열이 추가될 부분을 그냥 ?로 놔둔다.

이 부분들을 PreparedStatement가 해결해준다.

실행할 때 sql을 넘기는 것이 아니라 미리 준비해 둔다는 말이다.

 

        preparedStatement.setString(1, title);
        preparedStatement.setString(2, writer_id);
        preparedStatement.setString(3, content);
        preparedStatement.setString(4, files);

그러고 PreparedStatement의 set???(index, data)로 물음표를 채워준다.

여기서 조심할 점은 index가 0부터 시작하는 것이 아니라 1부터 시작한다는 점이다.

당연히 정수값을 넣으려면 setInt() 이런 느낌이다.

int result = preparedStatement.executeUpdate();

문장만 준비해두고 executeUpdate를 실행하면 업데이트가 된다.

이미 sql을 준비해두었기 때문에 sql을 넘기지 않는다.

import java.sql.*;

public class Program2 {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {

        String driver = "com.mysql.cj.jdbc.Driver";
        String url = "jdbc:mysql://localhost:3306/jdbc";
        String user = "root";
        String passwd = "12345678";

        String title = "seungkyubook";
        String writer_id = "sk";
        String content = "aaa";
        String files = "";

        String sql = "INSERT INTO NOTICE (TITLE, WRITER_ID, CONTENT, REGDATE, FILES) VALUES (?, ?, ?, NOW(),?)";

        Class.forName(driver);

        Connection connection = DriverManager.getConnection(url, user, passwd);

        PreparedStatement preparedStatement = connection.prepareStatement(sql);

        preparedStatement.setString(1, title);
        preparedStatement.setString(2, writer_id);
        preparedStatement.setString(3, content);
        preparedStatement.setString(4, files);

        int result = preparedStatement.executeUpdate();

        Statement statement = connection.createStatement();

        ResultSet resultSet = statement.executeQuery("SELECT * FROM NOTICE");

        while(resultSet.next()){
            int result_id = resultSet.getInt("ID");
            String result_title = resultSet.getString("TITLE");
            String result_writer_id = resultSet.getString("WRITER_ID");
            String result_content = resultSet.getString("CONTENT");
            Date result_regdate = resultSet.getDate("REGDATE");
            int result_hit = resultSet.getInt("HIT");
            String result_files = resultSet.getString("FILES");

            System.out.printf("id: %d, title: %s, write_id: %s, content: %s, regDate: %s, hit: %d, files: %s\n",
                    result_id, result_title, result_writer_id, result_content, result_regdate, result_hit, result_files);
        }

        preparedStatement.close();
        connection.close();
    }
}

최종적으로 작성한 코드이다.

데이터를 추가하고 바로 조회할 수 있도록 작성해보았다.

 

실행을 해보면

이렇게 잘 추가되는 것을 볼 수 있다.

 

  • 데이터 수정하기

수정은 그냥 PreparedStatement를 수정에 맞게 수정하면 된다.

import java.sql.*;

public class Program3 {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {

        String driver = "com.mysql.cj.jdbc.Driver";
        String url = "jdbc:mysql://localhost:3306/jdbc";
        String user = "root";
        String passwd = "12345678";

        int id = 6;
        String title = "seungkyu!!!";
        String writer_id = "sk";
        String content = "aaa";
        String files = "";

        String sql = "UPDATE NOTICE SET TITLE = ?, WRITER_ID = ?, CONTENT = ?, REGDATE = NOW(), FILES = ? WHERE ID = ?";

        Class.forName(driver);

        Connection connection = DriverManager.getConnection(url, user, passwd);

        PreparedStatement preparedStatement = connection.prepareStatement(sql);

        preparedStatement.setString(1, title);
        preparedStatement.setString(2, writer_id);
        preparedStatement.setString(3, content);
        preparedStatement.setString(4, files);
        preparedStatement.setInt(5, id);

        int result = preparedStatement.executeUpdate();

        Statement statement = connection.createStatement();

        ResultSet resultSet = statement.executeQuery("SELECT * FROM NOTICE");

        while(resultSet.next()){
            int result_id = resultSet.getInt("ID");
            String result_title = resultSet.getString("TITLE");
            String result_writer_id = resultSet.getString("WRITER_ID");
            String result_content = resultSet.getString("CONTENT");
            Date result_regdate = resultSet.getDate("REGDATE");
            int result_hit = resultSet.getInt("HIT");
            String result_files = resultSet.getString("FILES");

            System.out.printf("id: %d, title: %s, write_id: %s, content: %s, regDate: %s, hit: %d, files: %s\n",
                    result_id, result_title, result_writer_id, result_content, result_regdate, result_hit, result_files);
        }

        preparedStatement.close();
        connection.close();
    }
}

 

  • 데이터 삭제하기
import java.sql.*;

public class Program4 {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {

        String driver = "com.mysql.cj.jdbc.Driver";
        String url = "jdbc:mysql://localhost:3306/jdbc";
        String user = "root";
        String passwd = "12345678";

        int id = 6;

        String sql = "DELETE FROM NOTICE WHERE id = ?";

        Class.forName(driver);

        Connection connection = DriverManager.getConnection(url, user, passwd);

        PreparedStatement preparedStatement = connection.prepareStatement(sql);

        preparedStatement.setInt(1, id);


        preparedStatement.executeUpdate();

        Statement statement = connection.createStatement();

        ResultSet resultSet = statement.executeQuery("SELECT * FROM NOTICE");

        while(resultSet.next()){
            int result_id = resultSet.getInt("ID");
            String result_title = resultSet.getString("TITLE");
            String result_writer_id = resultSet.getString("WRITER_ID");
            String result_content = resultSet.getString("CONTENT");
            Date result_regdate = resultSet.getDate("REGDATE");
            int result_hit = resultSet.getInt("HIT");
            String result_files = resultSet.getString("FILES");

            System.out.printf("id: %d, title: %s, write_id: %s, content: %s, regDate: %s, hit: %d, files: %s\n",
                    result_id, result_title, result_writer_id, result_content, result_regdate, result_hit, result_files);
        }

        preparedStatement.close();
        connection.close();
    }
}

늘 그렇듯이 DELETE로 바꾸어주면 된다.

이렇게 모든 데이터가 지워진 것을 볼 수 있다.

 

  • CRUD 서비스 구현

배웠던 조회, 입력, 수정, 삭제를 이용하여 서비스를 만들어보자.

 

NOTICE를 대상으로 하기 때문에 패키지를 만들고 진행할 예정이다.

우선 데이터를 가져오고 저장할 NOTICE 클래스를 만들어보자.

package Service;

import java.util.Date;

public class Notice {
    private int id;
    private String title;
    private String writerId;
    private String content;
    private Date regDate;
    private int hit;
    private String files;

    public Notice(int id, String title, String writerId, String content, Date regDate, int hit, String files) {
        this.id = id;
        this.title = title;
        this.writerId = writerId;
        this.content = content;
        this.regDate = regDate;
        this.hit = hit;
        this.files = files;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getWriterId() {
        return writerId;
    }

    public void setWriterId(String writerId) {
        this.writerId = writerId;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public Date getRegDate() {
        return regDate;
    }

    public void setRegDate(Date regDate) {
        this.regDate = regDate;
    }

    public int getHit() {
        return hit;
    }

    public void setHit(int hit) {
        this.hit = hit;
    }

    public String getFiles() {
        return files;
    }

    public void setFiles(String files) {
        this.files = files;
    }
}

당연히 Private로 멤버들을 선언해주고, Getter와 Setter를 만들어준다.

 

전에 만들었던 조회 부분을 살짝 수정해서 getList를 완성해주었다.

package Service;

import java.sql.*;
import java.util.ArrayList;
import java.util.List;

public class NoticeService {

    String driver = "com.mysql.cj.jdbc.Driver";
    String url = "jdbc:mysql://localhost:3306/jdbc";
    String user = "root";
    String passwd = "12345678";

    public List<Notice> getList() throws ClassNotFoundException, SQLException {

        List<Notice> list = new ArrayList<Notice>();

        Class.forName(driver);

        Connection connection = DriverManager.getConnection(url, user, passwd);

        Statement statement = connection.createStatement();

        ResultSet resultSet = statement.executeQuery("SELECT * FROM NOTICE");

        while(resultSet.next()){
            int id = resultSet.getInt("ID");
            String title = resultSet.getString("TITLE");
            String writer_id = resultSet.getString("WRITER_ID");
            Date regDate = resultSet.getDate("REGDATE");
            String content = resultSet.getString("CONTENT");
            int hit = resultSet.getInt("HIT");
            String files = resultSet.getString("FILES");

            Notice notice = new Notice(id, title, writer_id, content, regDate, hit, files);

            list.add(notice);
        }
        resultSet.close();
        statement.close();
        connection.close();

        return list;
    }
}

다른 파일들도 복사한 후 코드를 약간 수정해서 메서드들을 작성했다.

package Service;

import java.sql.*;
import java.util.ArrayList;
import java.util.List;

public class NoticeService {

    String driver = "com.mysql.cj.jdbc.Driver";
    String url = "jdbc:mysql://localhost:3306/jdbc";
    String user = "root";
    String passwd = "12345678";

    public List<Notice> getList() throws ClassNotFoundException, SQLException {

        List<Notice> list = new ArrayList<>();

        Class.forName(driver);

        Connection connection = DriverManager.getConnection(url, user, passwd);

        Statement statement = connection.createStatement();

        ResultSet resultSet = statement.executeQuery("SELECT * FROM NOTICE");

        while(resultSet.next()){
            int id = resultSet.getInt("ID");
            String title = resultSet.getString("TITLE");
            String writer_id = resultSet.getString("WRITER_ID");
            Date regDate = resultSet.getDate("REGDATE");
            String content = resultSet.getString("CONTENT");
            int hit = resultSet.getInt("HIT");
            String files = resultSet.getString("FILES");

            Notice notice = new Notice(id, title, writer_id, content, regDate, hit, files);

            list.add(notice);
        }
        resultSet.close();
        statement.close();
        connection.close();

        return list;
    }

    public int insert(Notice notice) throws ClassNotFoundException, SQLException {
        String sql = "INSERT INTO NOTICE (TITLE, WRITER_ID, CONTENT, REGDATE, FILES) VALUES (?, ?, ?, NOW(),?)";

        String title = notice.getTitle();
        String writerId = notice.getWriterId();
        String content = notice.getContent();
        String files = notice.getFiles();

        Class.forName(driver);

        Connection connection = DriverManager.getConnection(url, user, passwd);

        PreparedStatement preparedStatement = connection.prepareStatement(sql);

        preparedStatement.setString(1, title);
        preparedStatement.setString(2, writerId);
        preparedStatement.setString(3, content);
        preparedStatement.setString(4, files);

        int result = preparedStatement.executeUpdate();


        preparedStatement.close();
        connection.close();

        return result;
    }

    public int update(Notice notice) throws ClassNotFoundException, SQLException {
        String sql = "UPDATE NOTICE SET TITLE = ?, WRITER_ID = ?, CONTENT = ?, REGDATE = NOW(), FILES = ? WHERE ID = ?";

        int id = notice.getId();
        String title = notice.getTitle();
        String writerId = notice.getWriterId();
        String content = notice.getContent();
        String files = notice.getFiles();

        Class.forName(driver);

        Connection connection = DriverManager.getConnection(url, user, passwd);

        PreparedStatement preparedStatement = connection.prepareStatement(sql);

        preparedStatement.setString(1, title);
        preparedStatement.setString(2, writerId);
        preparedStatement.setString(3, content);
        preparedStatement.setString(4, files);
        preparedStatement.setInt(5, id);

        int result = preparedStatement.executeUpdate();

        preparedStatement.close();
        connection.close();

        return result;
    }

    public int delete(int id) throws ClassNotFoundException, SQLException {
        String sql = "DELETE FROM NOTICE WHERE id = ?";

        Class.forName(driver);

        Connection connection = DriverManager.getConnection(url, user, passwd);
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        preparedStatement.setInt(1, id);

        int result = preparedStatement.executeUpdate();

        Statement statement = connection.createStatement();

        preparedStatement.close();
        connection.close();

        return result;
    }
}

'백엔드 > JDBC' 카테고리의 다른 글

JDBC 1일차  (0) 2023.03.12
728x90
  • JDBC란 무엇인가?

JDBC(Java Database Connectivity)는 자바에서 DB에 접속할 수 있도록 하는 자바의 API이다. JDBC는 데이터베이스에서 자료를 쿼리하거나 업데이트하는 방법을 제공한다.

자바와 DBMS 사이에서 통역을 해주는 통역사의 역할을 수행한다.

JDBC를 사용하면 개발자는 DBMS를 신경쓰지 않고 JDBC만을 사용해서 개발을 할 수 있게 된다.

JDBC를 사용하기 위해서는 해당 DBMS의 JDBC 드라이버를 사용해야 한다.

 

 

  • JDBC Driver

mac을 사용하고 있기 때문에

 

https://dev.mysql.com/downloads/connector/j/

 

MySQL :: Download Connector/J

MySQL Connector/J 8.0 is highly recommended for use with MySQL Server 8.0, 5.7 and 5.6. Please upgrade to MySQL Connector/J 8.0.

dev.mysql.com

해당 사이트에서

Platform Independent를 선택해서 다운 받아준다.

 

다운로드 받은 후 압축을 풀어둔다.

 

이제 실습을 하기 위한 Intellij를 열어주자.

새로운 프로젝트를 생성하고 Project Structure에서 다운로드 받고 압축을 해제했던 connector를 추가해준다.

OK를 누르면 이제 JDBC를 사용할 준비가 된 것이다.

 

  • JDBC 기본코드

우선은 MySQL에서 받은 드라이버를 로드해야 한다.

 

그렇기에 드라이버를 로드하는 것이 가장 첫번째 코드이다.

Class.forName("com.mysql.cj.jdbc.driver");

이 코드로 MySQL 드라이버를 로드하자.

 

이렇게 드라이버를 로드하면 메모리에 잡히게 된다.

 

이 드라이버를 연결해 연결 객체를 얻어야 한다.

Connection connection = DriverManager.getConnection(...);

현재 로컬에 있는 데이터베이스에 연결할 것이기 때문에

Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/DB명", "user", "password");

이렇게 연결하도록 하자.

 

실행도구를 생성한다.

Statement statement = connection.createStatement();

 

쿼리를 실행하고 결과를 받아온다.

ResultSet resultSet = statement.executeQuery(sql);

 

현재 ResultSet은 파일의 시작(Before of File)을 가르키고 있으며, 이 ResultSet은 ResultSet.next를 이용하여 다음 정보들을 넘겨 받게 된다.

그러다가 파일의 마지막(End of File)을 가르키면 더 이상 데이터를 반환하지 않게 된다.

 

ResultSet.getXXX("열이름") 메서드를 사용하면 현재 ResultSet이 가르키고 있는 행의 열 이름에 해당하는 정보를 가져오게 된다.

 

바로 실습을 해보도록 하자.

참고로 이 부분에 대한 코드는 거의 고정이니 외워서 사용하도록 하자.

Class.forName("com.mysql.cj.jdbc.Driver");

Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbc", "root", "12345678");

Statement statement = connection.createStatement();

ResultSet resultSet = statement.executeQuery("");
        
//
//
        
resultSet.close();
statement.close();
connection.close();

우선 DB에서 데이터를 가져오기 위해 데이터를 추가한다.

 

INSERT INTO NOTICE VALUES
(
    1,
    "hello",
    "sk",
    "aaa",
    SYSDATE(),
    0,
    ""
);

이렇게 NOTICE 테이블에 데이터를 대충 추가한 후

 

import java.sql.*;

public class Main {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {

        Class.forName("com.mysql.cj.jdbc.Driver");

        Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbc", "root", "12345678");

        Statement statement = connection.createStatement();

        ResultSet resultSet = statement.executeQuery("SELECT * FROM NOTICE");



        if(resultSet.next()){
            String title = resultSet.getString("TITLE");
            System.out.println(title);
        }

        resultSet.close();
        statement.close();
        connection.close();
    }
}

ResultSet에서 다음 데이터를 가져온 후 title 열의 데이터를 출력해보면

 

이렇게 잘 출력이 되는 것을 볼 수 있다.

 

데이터를 하나 더 추가하여 while 문으로 모든 데이터를 출력한 모습이다.

당연히 MySQL과 마찬가지로 쿼리에 WHERE 문도 사용 할 수가 있다.

import java.sql.*;

public class Main {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {

        Class.forName("com.mysql.cj.jdbc.Driver");

        Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbc", "root", "12345678");

        Statement statement = connection.createStatement();

        ResultSet resultSet = statement.executeQuery("SELECT * FROM NOTICE WHERE ID > 1");

        while(resultSet.next()){
            int id = resultSet.getInt("ID");
            String title = resultSet.getString("TITLE");
            String writer_id = resultSet.getString("WRITER_ID");
            Date regDate = resultSet.getDate("REGDATE");
            String content = resultSet.getString("CONTENT");
            int hit = resultSet.getInt("HIT");

            System.out.printf("id: %d, title: %s, write_id: %s, regDate: %s, content: %s, hit: %d\n", id, title, writer_id, regDate, content, hit);
        }

        resultSet.close();
        statement.close();
        connection.close();
    }
}

'백엔드 > JDBC' 카테고리의 다른 글

JDBC 2일차  (0) 2023.03.14
728x90

이번엔 캐시에 대해서 더 자세히 배운다.

 

저번 시간에 캐시에 대해 대충은 공부를 했을 것이다.

캐시가 없을 때는 헤더와 바디에 해당하는 내용을 몇번이고 다시 전송해야 한다.

인터넷 네트워크가 메모리와 디스크에 비해 느리기 때문에 사용자가 기다리는 시간이 길어진다.

 

여기에 캐시를 추가해보자.

이렇게 캐시를 추가하면 캐시가 유효한 60초 동안은 HTTP 바디에 해당하는 부분은 전송할 필요가 없다.

그렇게 되면 사용자가 기다리는 시간이 훨씬 줄어들게 된다.

 

근데 만약 60초의 시간이 지나간 후에는 어떻게 될까?

당연히 서버에서 데이터를 다시 조회하고 캐시를 갱신해야 할 것 같다.

하지만 서버에서도 데이터가 변하지 않았다면 굳이 캐시를 갱신해야할까?

 

  • 캐시 시간 초과

캐시 만료후에도 서버에서 데이터를 변경하지 않았다면, 저장해두었던 캐시를 재사용 할 수 있을 것이다.

하지만 서버에서 데이터가 변하지 않았다고 확인을 받아야 할 것이다.

 

그 방법에 대해 살펴보자.

이렇게 HTTP 헤더에 그 데이터가 마지막으로 수정된 시각을 포함해서 보낸다.

 

그리고 그 다음에 GET으로 조회를 한다면 요청에 마지막 수정된 시각을 포함해서 확인을 받는다.

만약 서버에서 확인을 한 후 변동이 없다면 304로 데이터가 수정되지 않았음을 알리고 해당 캐시데이터를 재사용한다.

 

이렇게 하면 데이터가 상대적으로 작은 헤더끼리만 보내도 데이터를 사용할 수 있어 매우 실용적인 해결책이다.

 

이 방법도 굉장히 좋지만 여기에 추가로, 같은 데이터를 수정해서 데이터는 같지만 수정된 날짜만 다른 경우도 생각을 해보자.

이런 경우 캐시를 사용하면 되지만, 서버에서는 용량이 상대적으로 큰 데이터를 전송해주게 될 것이다.

 

그렇기에 ETag라는 검증 헤더를 추가하게 된다.

ETag(Entity Tag)는 캐시용 데이터에 고유한 이름을 달아두고, 데이터가 변경될 때마다 이 이름을 바꾼다.

그냥 서버에서 ETag를 확인하고 현재 데이터의 ETag와 같으면 캐시를 사용하고, 다르면 HTTP 바디 내용을 재전송하는 간단한 방식이다.

위에 방법처럼 서버에서 응답할 때 ETag를 같이 캐시에 저장한 후

다시 클라이언트에서 요청할 경우 ETag를 같이 확인 받는 방법이다.

이렇게 캐시를 확인 받을 수 있다.

 

  • 캐시와 조건부 요청 헤더

Cache-Control

- Cache-Control: max-age

캐시 유효 시간(초)

- Cache-Control: no-cache

데이터는 캐시해도 되지만, 항상 origin 서버에 검증한 후에 사용

- Cache-ControlL no-store

데이터를 저장하면 안됨

 

Validator(검증 헤더)

- ETag

위에 보았듯이 ETag를 확인

- Last-Modified

마지막 수정시간을 확인

 

조건부 요청 헤더

- If-Match, If-None-Match

ETag를 확인하여 조건부로 HTTP 메시지 바디까지 전송 요청

- If-Modified-Since, If-Unmodified-Since

Last-Modified를 확인하여 조건부로 HTTP 메시지 바디까지 전송 요청

 

  • 캐시 무효화

Cache-Control: no-cache, no-store, must-revalidate를 사용한다.

 

- Cache-Control: no-cache

데이터는 캐시해도 되지만, 항상 Origin 서버에 검증하고 사용

- Cache-Control: no-store

데이터를 저장하면 안됨

- Cache-Control: must-revalidate

Origin 서버 접근 실패시 반드시 오류가 발생

'백엔드 > HTTP' 카테고리의 다른 글

HTTP 7일차  (0) 2023.03.10
HTTP 6일차  (0) 2023.03.09
HTTP 5일차  (1) 2023.03.09
HTTP 4일차  (0) 2023.03.08
HTTP 3일차  (0) 2023.03.06
728x90

HTTP 헤더에 대해서 공부하자.

 

HTTP 헤더는 지금까지 나왔겠지만

해당 부분들을 말한다.

HTTP 헤더는 HTTP 전송에 필요한 모든 부가정보를 담고 있다.

 

헤더는 데이터를 해석할 수 있는 정보를 제공하게 되었다. (데이터 유형, 데이터 길이...)

 

Content-Type

표현 데이터의 형식을 담고 있다.

예로는 text/html; charset=utf-8, application/json 등이 있다.

 

Content-Encoding

표현 데이터를 압축하기 위해 사용한다.

데이터를 전달하는 곳에서 압축 후 인코딩 헤더를 추가한다.

예로는 gzip, deflate 등이 있다.

 

Content-Language

표현 데이터의 언어를 표현한다.

예로는 ko, en 등이 있다.

 

Content-Length

표현 데이터의 길이를 표현한다.

데이터는 바이트 단위로 표현한다.

 

  • 콘텐츠 협상

클라이언트는 서버에 원하는 표현을 요청할 수 있다.

이 협상 헤더는 요청 시에만 사용할 수 있다.

 

Accept: 클라이언트가 선호하는 미디어 타입을 전달

Accept - Charset: 클라이언트가 선호하는 문자 인코딩을 전달

Accept - Encoding: 클라이언트가 선호하는 압축 인코딩을 전달

Accept - Language: 클라이언트가 선호하는 자연 언어를 전달

 

하나의 표현만 요청하는 것이 아니라 여러 개의 값들을 우선순위로 지정해 요청할 수 있다.

0 ~ 1의 값을 사용하며 클수록 높은 우선순위를 가진다. (생략하면 1)

GET /page
Accept-Language: ko-KR,ko;q=0.8,en-US;q=0.8

이런 식으로 사용한다.

 

그리고 당연히 늘 그렇듯이 더 구체적인 코드를 우선한다.

GET /page
Accept: text/*, text/plain, */*

이렇게 되어 있다면

1. text/plain

2. text/*

3. */*

이 우선순위로 따라가게 된다.

 

  • 전송 방식

전송 방식으로는 단순 전송, 압축 전송, 분할 전송, 범위 전송 4가지가 있다.

 

단순 전송

HTTP/1.1 200 OK
Content-Type: text/html;charset=UTF-8
Content-Length: 3456

<html>
	<body>...</body>
</html>

그냥 단순하게 이런 코드를 전송하는 것이다.

Content-Length를 적어주어야 한다.

 

압축 전송

HTTP/1.1 200 OK
Content-Type: text/html;charset=UTF-8
Content-Encoding: gzip
Content-Length: 521

asdfasdfgh42523yhshadsgaasdfds...

말 그대로 압축해서 보내는 것이다.

압축을 했기 때문에 Content-Encoding으로 어떻게 압축했는지 알려주어야 한다.

 

분할 전송

HTTP/1.1 200 OK
Content-Type: text
Transfer-Encoding: chunked

2
My
4
Name
2
is
2
sk

이렇게 하나의 데이터를 보낼 때마다 바이트 수와 분할해서 보내는 것이다.

처음부터 Content-Length로 바이트 수를 알려주면 안 된다.

 

범위 전송

클라이언트가 특정 범위를 요청할 때 보내는 방법으로

만약 클라이언트가

GET /page
Range: bytes=501-1000

이런 요청을 보냈다면 그 범위에 맞게

HTTP/1.1 200 OK
Content-Type: text/plain
Content-Range: byes 501-1000/1000

adsfsdafasdfasdfahsdfn

해당 범위의 데이터만 응답으로 보내는 것이다.

 

  • 일반 정보

그냥 일반 정보로는

Form: 유저의 이메일 정보

Referer: 이전 웹 페이지 주소

User-Agent: 유저 에이전트 애플리케이션 정보

Server: 요청을 처리하는 origin 서버의 소프트웨어 정보

Date: 메시지가 생성된 날짜

가 있다.

 

From

요청에서 사용하며, 검색 엔진들이 주로 사용한다.

일반적으로는 많이 사용하지 않는다.

 

Referer

현재 요청된 페이지의 이전 웹 페이지 주소이며, 요청에서 사용한다.

Referer를 사용해서 유입 경로를 분석한다.

 

User-Agent

클라이언트의 애플리케이션 정보이며, 요청에서 사용한다.

어떤 종류의 브라우저에서 장애가 발생하는지 파악 가능하다.

 

Server

서버의 소프트웨어 정보로, 응답에서 사용한다.

 

Date

메시지가 발생한 날짜와 시간으로, 응답에서 사용한다.

 

  • 특별한 정보

Host: 요청한 호스트 정보

Location: 페이지 리다이렉션

Allow: 허용 가능한 HTTP 메서드

Retry-After: 유저가 다음 요청을 하기까지 기다려야 하는 시간

 

특별한 헤더로는 이렇게 있으며, 공통부분이 없으니 그냥 하나씩 바로 알아보자.

 

Host

요청한 호스트 정보를 적어주어야 해당 서버의 어떤 도메인으로 접근할 것인지를 알 수 있다.

 

Location

페이지 리다이렉션으로 웹 브라우저는 3xx 응답의 결과에 Location 헤더가 있으면, 그 위치로 자동 이동한다.

 

Allow

허용 가능한 HTTP 메서드를 알려주며, 메서드 오류인 405(Method Not Allowed) 응답에 포함되어야 한다.

 

Retry-After

요청을 처리할 수 있는 시간을 알려주며, 503(Service Unavailable)에 포함한다.

Retry-After: Sat, 4 Dec 1999 10:12:56 GMT

 

  • 쿠키

쿠키가 무엇인지는 저번 JSP 공부에서 배웠을 것이다.

만약 쿠키를 사용하지 않는다면 로그인 후 해당 도메인의 다른 페이지에 접근할 때 서버에서 유저를 기억하지 못할 것이다.

클라이언트와 서버가 요청과 응답을 주고 받으면 연결이 끊어지고, 서버는 이전 요청을 기억하지 못한다.

 

그렇다고 모든 GET에 사용자의 정보를 쿼리를 넘길 수도 없을 것이다.

 

그렇기 때문에 클라이언트에 쿠키를 남긴다.

이렇게 Set-Cookie로 쿠키를 저장하고 나면 쿠키 저장소에서 조회해가며 Cookie를 이용해 헤더에 추가한다.

 

쿠키는 네트워크 트래픽을 추가로 유발하기 때문에 최소한의 정보만 사용하는 것이 좋다.

보안에 민감한 데이터는 저장하면 안된다.

 

Expries, max-age로 쿠키의 생명 주기를 설정할 수 있다.

Set-Cookie: expires=Sat, 4-Dec-1999 10:19:21 GMT

만료일이 되면 쿠키를 삭제한다.

 

Set-Cookie: max-age=3600

0이나 음수를 지정하면 쿠키 삭제

 

도메인

쿠키의 도메인을 명시한다면 명시한 문서 기준 도메인 + 서브 도메인 포함하여 쿠키가 접근한다.

쿠키의 도메인을 명시하지 않는다면 현재 문서의 도메인만 적용하여 쿠키가 접근한다.

예를 들면 domain=seungkyu.com을 지정하면

seungkyu.com은 물론이고 ???.seungkyu.com에도 쿠키가 접근한다.

하지만 생략을 한다면 seungkyu.com에만 접근하게 된다.

그리고 지정한 경로의 하위 페이지에만 쿠키가 접근하게 된다.

 

완성된 Set-cookie의 예를 보자면

Set-Cookie: id=1232145; expires=Sat, 4-Dec-1999 12:04:11 GMT; path=/; domain=seungkyu.com;Secure

이런식으로 작성이 되게 된다.

'백엔드 > HTTP' 카테고리의 다른 글

HTTP 8일차  (0) 2023.03.10
HTTP 6일차  (0) 2023.03.09
HTTP 5일차  (1) 2023.03.09
HTTP 4일차  (0) 2023.03.08
HTTP 3일차  (0) 2023.03.06
728x90

HTTP 상태코드에 대해서 배워보자.

 

  • 상태 코드

상태코드는 서버에서 클라이언트에게 보내며, 클라이언트가 보낸 요청에 대한 응답의 상태를 알려준다.

 

- 1XX (Informational) : 요청이 수신되어 처리 중인 상태

- 2XX (Successful) : 요청 정상 처리 상태

- 3XX (Redirection) : 요청을 완료하려면 추가 행동이 필요한 상태

- 4XX (Client Error) : 클라이언트 오류, 잘못된 문법 등으로 서버가 요청을 수행할 수 없는 상태

- 5XX (Server Error) : 서버 오류, 서버가 요청을 처리하지 못하는 상태

 

1XX (Informational)

요청이 수신되어 현재 처리 중인 상태로, 현재는 거의 사용하지 않는다고 한다.

 

2XX (Successful)

클라이언트의 요청을 정상적으로 처리한 상태이다.

 

- 200 OK

가장 평범한, 요청 성공이다.

- 201 Created

요청에 성공해서 새로운 리소스가 생성이 된다.

- 202 Accepted

요청은 접수 되었으나, 처리가 완료되지 않은 상태이다.

- 204 No Content

서버가 요청을 정상적으로 수행했지만, 응답으로 보낼 데이터는 없다.

버튼을 눌러도 같은 화면을 유지해야 하는 상황에서 사용하며, 결과 내용이 없어도 204만으로도 성공을 인식할 수 있다.

 

3XX (Redirection)

요청을 완료하기 위해 추가적인 조치가 필요한 상태이다.

웹 브라우저는 3XX 응답의 결과에 Location 헤더가 있으면, 그 위치로 자동 이동한다.

 

- 300 Multiple Choices

- 301 Moved Permanently

- 302 Found

- 303 See Other

- 304 Not Modified

- 307 Temporary Redirect

- 308 Permanent Redirect

 

일단 흐름을 이해하면 이렇게 된다.

Location에 있는 페이지로 이동한다.

 

리다이렉션의 종류에는

- 영구 리다이렉션 (301, 308)

특정 리소스의 URI가 영구적으로 이동했을 때

원래의 URL을 사용하지 않으며, 검색 엔진 등에서도 변경을 인지한다.

301은 Moved Permanently로 리다이렉트 요청 메서드가 GET으로 변하고, 바디 부분이 제거될 수 있다.

308은 Permanent Redirt로 리다이렉트시 요청 메서도와 본문이 유지된다.

- 일시 리다이렉션 (302, 307, 303)

리소스의 URI가 일시적으로 변경된 상태이다.

302는 Found로 리다이렉트시 요청 메서드가 GET으로 변하고, 본문이 제거될 수 있다.

307는 Temporary Redirect로 리다이렉트시 요청 메서드와 본문을 유지한다.

303은 See Other로 리다이렉트시 요청 메서드가 GET으로 변경

 

만약 POST 방식으로 결제를 한 후 웹 브라우저를 새로고침하면 중복 주문이 되는 문제가 생길 수 있다.

이렇게 DB에 주문데이터가 중복으로 2개 들어간다.

이 문제를 해결하기 위해서 POST로 결재 후에 결재 화면을 GET 메서드로 리다이렉트한다.

그러면 POST로 가지 않기 때문에 중복 결재가 되는 일이 없어진다. (조회만 하기 때문에)

- 특수 리다이렉션

304는 Not Modified로 캐시 목적으로 사용한다.

클라이언트에게 리소스가 수정되지 않았음을 알려, 로컬에 저장된 캐시를 재사용하게 한다.

 

4XX (Client Error)

클라이언트 요청에 따라 잘못된 문법등으로 서버가 요청을 수행할 수 없는 상태이다.

오류의 원인이 클라이언트에 있을 때 4XX 오류가 발생한다.

 

400은 Bad Request로 클라이언트가 잘못된 요청을 해서 서버가 요청을 처리할 수 없을 때이다.

401은 Unauthorized로 클라이언트가 해당 리소스에 대한 인증이 필요한 상태이다.

403은 Forbidden로 서버가 요청을 이해했지만 승인을 거부한 상태이다.

404은 Not Found로 요청 리소스를 찾을 수 없는 상태이다.

 

 5XX (Servor Error)

서버 문제로 오류가 발생한 모든 경우이다.

의도적으로 만드는 페이지는 아닐 것이다.

'백엔드 > HTTP' 카테고리의 다른 글

HTTP 8일차  (0) 2023.03.10
HTTP 7일차  (0) 2023.03.10
HTTP 5일차  (1) 2023.03.09
HTTP 4일차  (0) 2023.03.08
HTTP 3일차  (0) 2023.03.06
728x90

HTTP 메서드로 API를 설계해보자.

 

  • 서버로 데이터 전송

서버로 데이터를 전달하는 방식은 2가지이다.

쿼리 파라미터, 메시지 바디 두 가지이지만 사실 GET과 GET을 제외한 나머지이다.

 

- 쿼리 파라미터

GET

데이터를 조회할 때 사용

 

- 메시지 바디

POST, PUT, PATCH

회원 가입, 상품 주문, 리소스 등록

 

클라이언트에서 서버로 데이터를 전송하는 상황은 데이터를 조회하거나, HTML form을 통한 데이터 전송, HTTP API를 통한 데이터 전송할 때이다.

차례로 살펴보자.

 

- 정적 데이터 조회

어차피 정적이기 때문에 쿼리 파라미터를 사용하지 않는다.

 

- 동적 데이터 조회

정적 데이터 조회와는 다르게 쿼리 파라미터를 사용하여 동적으로 결과를 받아온다.

 

- HTML form을 이용하여 데이터 전송

우선 HTML Form 전송은 GET, POST만 지원을 한다.

그러니 조회를 제외한 대부분은 POST를 사용한다.

 

POST 방식으로 파일도 전송이 가능하다.

이렇게 다른 종류의 여러 파일과 폼의 내용을 함께 전송하려면 Content-Type: multipart/form-data를 사용한다.

 

-HTTP API 데이터 전송

보통 이렇게 API 데이터만 전송하는 경우는 서버 to 서버로 사람이 보지 않을 때이다.

Content-type: application/json이 사실상 표준이 되었다.

 

  • HTTP API 설계

우선 등록을 POST로 하는 POST 기반 등록부터 알아보자.

 

POST 기반 등록

- 학생 목록 /students -> GET

- 학생 등록 /students -> POST

- 학생 조회 /students/{id} -> GET

- 학생 수정 /students/{id} -> PATCH, PUT, POST

- 학생 삭제 /students/{id} -> DELETE

 

클라이언트는 등록될 리소스의 URI를 모르기 때문에 서버가 새로운 리소스 URI를 생성해준다.

그리고 이렇게 만든 리소스를 관리하는 디렉토리를 컬렉션이라고 한다.

 

PUT 기반 등록

- 파일 목록 /files -> GET

- 파일 조회 /files/{filename} -> GET

- 파일 등록 /files/{filename} -> PUT

- 파일 삭제 /files/{filename} -> DELETE

- 파일 ???  /files -> POST

 

POST 기반 등록과는 다르게 클라이언트가 리소스의 URI를 알고 있어야 한다.

클라이언트가 직접 리소스의 URI를 지정하기 때문에 클라이언트가 리소스를 관리하고 그 저장소를 스토어라고 한다.

 

HTML FORM 사용

- 학생 목록 /students -> GET

- 학생 목록 폼 /students/new -> GET

- 학생 등록 /students/new -> POST

- 학생 조회 /students/{id} -> GET

- 학생 수정 폼 /students/{id}/edit -> GET

- 학생 수정 /students/{id}/edit -> POST

- 학생 삭제 /students/{id}/delete -> POST

 

HTML FORM은 GET, POST만 지원한다.

그렇기 때문에 어쩔 수 없이 동사로 된 리소스 경로를 사용한다.

HTTP 메서드로도 어쩔 수 없는 경우에만 사용한다.

'백엔드 > HTTP' 카테고리의 다른 글

HTTP 7일차  (0) 2023.03.10
HTTP 6일차  (0) 2023.03.09
HTTP 4일차  (0) 2023.03.08
HTTP 3일차  (0) 2023.03.06
HTTP 2일차  (0) 2023.03.02
728x90

HTTP 메서드에 대해 알아본다.

 

  • HTTP API를 만들어보자

만약 학생 정보 관리 API를 만들어라 라고 한다면

지금의 우리는 URI를

- 학생 목록 조회 /read-member-list

- 학생 조회 /read-member-by-id

- 학생 등록 /create-member

- 학생 수정 /update-member

- 학생 삭제 /delete-member

 

이렇게 설계할 것이다.

 

하지만 이렇게 설계하는 것은 좋지 않은 방법이다.

리소스가 식별이 되도록 설계를 해야한다.

여기서는 '학생 목록 조회'가 리소스가 아니라, 학생 그 자체가 리소스이다.

조회든, 수정이든 신경쓰지 않고 학생이라는 리소스만 식별하면 되기 때문에 학생 리소스를 URI에 매핑한다.

그럼 이렇게 리소스가 학생이라는 것을 알았는데, 조회나 수정은 어떻게 해야할까?

우리는 이 행위를 메서드에 넘기게 된다.

 

  • HTTP 메서드

HTTP 주요 메서드 종류에는

GET : 리소스 조회

POST : 요청 데이터 처리, 주로 등록에 사용

PUT : 리소스를 대체, 해당 리소스가 없으면 생성

PATCH : 리소스 부분 변경

DELETE : 리소스 삭제

가 있다.

 

GET

리소스 조회

서버에 전달하고 싶은 데이터를 query를 통해 전달한다.

 

이렇게 서버에 리소스 조회를 한다.

 

POST

요청 데이터 처리

메시지 바디를 통해 서버로 요청 데이터를 전달하며, 들어온 데이터를 처리하는 모든 기능을 수행한다.

POST는 서버가 아직 식별하지 않은 새 리소스를 생성하거나 요청 데이터를 처리하거나, 아니면 다른 메서드로 처리하기 애매한 경우에 사용한다. (거의 만능이라고 한다)

 

PUT

기존에 있던 리소스를 대체한다.

리소스가 있으면 대체하고 리소스가 없으면 생성한다.

POST와의 차이점은 PUT은 클라이언트가 리소스의 위치를 알고 URI로 지정한다.

 

리소스를 대체하는 경우만 살펴보자.

 

PATCH

PUT과 같이 대체하는 것이 아니라 부분만 변경한다.

 

DELETE

리소스를 제거해버린다.

 

HTTP 메서드의 속성

- 안전

호출해도 리소스를 변경하지 않는다.

- Idempotent

호출 횟수에 관계 없이 같은 결과가 나온다.

- 캐시 가능

응답 결과 리소스를 캐시해서 사용해도 되는가?

 

이러한 속성들이 있다.

각각의 메서드들에 살펴보면

HTTP 메서드 안전 Idempotent 캐시 가능
GET O O O
POST X X O
PUT X O X
DELETE X O X
PATCH X X O

 

'백엔드 > HTTP' 카테고리의 다른 글

HTTP 6일차  (0) 2023.03.09
HTTP 5일차  (1) 2023.03.09
HTTP 3일차  (0) 2023.03.06
HTTP 2일차  (0) 2023.03.02
HTTP 1일차  (0) 2023.03.01

+ Recent posts