프로젝트/게시판 만들기

스프링부트 - 게시판 만들기 2탄

반기반개퐝퐝 2024. 11. 16. 17:50

지난 글에 이어 2탄입니다.

 

mariadb를 연결하는 과정은

도뎡님의 게시물을 참고해 주시고

https://congsong.tistory.com/14

 

스프링 부트(Spring Boot) - 게시판 MariaDB(HikariCP) 연동하기 [Thymeleaf, MariaDB, IntelliJ, Gradle, MyBatis]

본 게시판 프로젝트는 단계별(step by step)로 진행되니, 이전 단계를 진행하시는 것을 권장드립니다. 본 포스팅은 DBeaver를 기준으로 작성된 글이며, 만약 MariaDB가 설치되어 있지 않으시다면, 선행

congsong.tistory.com

 

저는 그 다음 단계로 넘어갑니다!


4. 게시판 MariaDB(HikariCP) 연동하기

https://congsong.tistory.com/14

 

스프링 부트(Spring Boot) - 게시판 MariaDB(HikariCP) 연동하기 [Thymeleaf, MariaDB, IntelliJ, Gradle, MyBatis]

본 게시판 프로젝트는 단계별(step by step)로 진행되니, 이전 단계를 진행하시는 것을 권장드립니다. 본 포스팅은 DBeaver를 기준으로 작성된 글이며, 만약 MariaDB가 설치되어 있지 않으시다면, 선행

congsong.tistory.com

 

DB와의 통신을 할 수 있게 작업을 진행합니다.

생소한 단어들과 개념들이 많이 있던 과정이었습니다.

 

도뎡님의 글을 인용해서 개념을 좀 정리하였고,

더 궁금한 내용은 추가로 작성했습니다.

 

1. DB 커넥션 생성하기

  • Server Host
    • 데이터베이스를 연결할 주소를 의미합니다. 우리는 자신의 컴퓨터를 의미하는 로컬 호스트(localhost)에서 개발하며, 주소는 localhost 또는 127.0.0.1을 입력합니다.
  • Port
    • MariaDB의 기본 포트 번호는 3306(MySQL과 동일)
  • Username
    • 데이터베이스의 마스터 계정
    • MariaDB 설치 단계에서 셋팅한 계정(주로 root)
  • Password
    • MariaDB 설치시 입력했던 패스워드
 패스워드가 기억이 안 나서 좀 헤맸었습니다 하핫
  • Database
    • 커넥션에서 기본적으로 사용할 스키마(Schema)를 의미합니다.

 

2. 스키마 생성하기

= table 생성하기

먼저 스키마란? :
: 데이터베이스의 구조와 제약조건에 관해 전반적인 명세를 기술한 것을 말한다.
  좀 더 자세하게 말하자면, 개체의 특성을 나타내는

-- 속성(Attribute)
속성들의 집합으로 이루어진
-- 개체(Entity)
개체 사이에 존재하는
-- 관계(Relation)

에 대한 정의와 이 것들이 유지해야 할 제약조건을 기술한 것이 바로 스키마다. 즉, 데이터베이스 내에 어떤 구조로 데이터가 저장되는지를 나타내는 데이터베이스 구조를 스키마라고 칭한다.

출처 : https://itkjspo56.tistory.com/94

 

[SQL] 스키마란 무엇인가?

스키마(Schema)란 무엇인가? Database를 공부하다 보면 쉽게 발견할 수 있는 용어 스키마. 스키마의 종류에는 무엇이 있는지 한 번 살펴보자 먼저 스키마란? : 데이터베이스의 구조와 제약조건에 관

itkjspo56.tistory.com

3. 기본 DB(Default Database) 설정하기

해당 내용은 skip!

 

4. 데이터소스(DataSource) 설정하기

해당 부분이 이번 챕터의 핵심입니다!

 

데이터 소스는 DB와의 커넥션을 관리해 주는 인터페이스입니다.

데이터 소스 설정은 대표적으로 두 가지 방법을 이용할 수 있습니다.

  1.  application.properties에 DB 정보를 선언해 두고, 설정(Configuration) 파일에서 참조하는 방법
  2. 설정(Configuration) 파일에서 DB 정보를 직접 입력하는 방법

우리는 이 중 첫번째 방법을 통해 데이터 소스 빈(Bean)을 구성합니다.

 

src/main/resources/application.properties

spring.datasource.hikari.driver-class-name=org.mariadb.jdbc.Driver
spring.datasource.hikari.jdbc-url=jdbc:mariadb://localhost:3306/board?serverTimezone=Asia/Seoul&useUnicode=true&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true
spring.datasource.hikari.username=root
spring.datasource.hikari.password=1234
spring.datasource.hikari.connection-test-query=SELECT NOW() FROM dual

 

  • jdbc-url
    • 데이터베이스의 주소를 의미합니다. 
    • 포트번호(3306) 뒤의 board는 애플리케이션에서 참조할 DB의 이름이며, serverTimezone 등의 파라미터는 시간, 한글 처리 등의 기본적인 설정을 처리하는 용도의 파라미터입니다.
JDBC란?

JDBC(Java Database Connectivity)는 Java 기반 애플리케이션의 데이터를 데이터베이스에 저장 및 업데이트하거나, 데이터베이스에 저장된 데이터를 Java에서 사용할 수 있도록 하는 자바 API이다.

JDBC는 Java 애플리케이션에서 데이터베이스에 접근하기 위해 JDBC API를 사용하여 데이터베이스에 연동할 수 있으며, 데이터베이스에서 자료를 쿼리(Query)하거나 업데이트하는 방법을 제공한다.

출처 : https://ittrue.tistory.com/250

 

[Java] JDBC란 무엇인가? - Java Database Connectivity

JDBC란? JDBC(Java Database Connectivity)는 Java 기반 애플리케이션의 데이터를 데이터베이스에 저장 및 업데이트하거나, 데이터베이스에 저장된 데이터를 Java에서 사용할 수 있도록 하는 자바 API이다. JDB

ittrue.tistory.com

 

5. 데이터 소스 설정(Datasource Configuration) 클래스 추가하기

java 기반의 설정 파일(클래스에 @Configuration 어노테이션 추가)

-> 데이터 소스 객체(Bean)을 관리해 줄 설정(Configuration) 클래스 필요

1) 패키지 클래스 추가하기

package com.study.config;

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

import javax.sql.DataSource;

@Configuration
@PropertySource("classpath:/application.properties")
public class DatabaseConfig {

    @Autowired
    private ApplicationContext context;
    //스프링컨테이너 설정

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.hikari")
    //prefix에 해당하는 spring.datasource.hikari로 시작하는 설정을 모두 읽어 들여 해당 메서드에 매핑(바인딩)하는 개념
    public HikariConfig hikariConfig() {
        return new HikariConfig();
    }
    //히카리CP객체 생성

    @Bean
    public DataSource dataSource() {
        return new HikariDataSource(hikariConfig());
    }

    @Bean
    public SqlSessionFactory sqlSessionFactory() throws Exception {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(dataSource());
//		factoryBean.setMapperLocations(context.getResources("classpath:/mappers/**/*Mapper.xml"));
        return factoryBean.getObject();
    }

    @Bean
    public SqlSessionTemplate sqlSession() throws Exception {
        return new SqlSessionTemplate(sqlSessionFactory());
    }

}
  • Configuration
    • 스프링은 @Configuration이 선언된 클래스를 자바(java) 기반 설정 파일로 인식
  • @PropertySource
    • 해당 클래스에서 참조할 properties 경로 선언
  • @Autowired
    • 빈(Bean)으로 등록된 인스턴스(객체)를 클래스에 주입(예. @Resource, @Inject 등)
  • ApplicationContext
    • 스프링 컨테이너 중 하나
      • 스프링 컨테이너 : 빈(Bean)의 생성과 사용 관계, 생명 주기 등 관리
      • 빈(Bean)
        • Java 객체
        • 결합도가 높은 문제를 컨테이너에서 빈을 주입받는 방식으로 해결
        • 클래스간 의존성 낮추
스프링 컨테이너(Spring Container)

스프링 컨테이너는 스프링에서 자바 객체들을 관리하는 공간을 말합니다.
자바 객체를 스프링에선 빈(Bean)이라고 하는데, 스프링 컨테이너에서는 이 빈의 생성부터 소멸까지를 개발자 대신 관리해주는 곳이라고 할 수 있습니다.
컨테이너는 크게 두 종류로 나눌 수 있습니다. 하나는 BeanFactory이고, 다른 하나는 ApplicationContext입니다. ApplicationContext컨테이너가 BeanFactory의 기능을 포괄하면서 추가적인 기능을 제공하기 때문에 대부분의 경우에는 ApplicationContext를 사용합니다. 그렇다면 어떤 부분이 ApplicationContext에 추가가 되었을까요? 이벤트 발행 및 구독 모델 지원 및 리소스 조회 편의성 제공, 환경변수를 구분해주는 기능 및 언어 국제화를 지원합니다. 스프링 컨테이너에 객체, 빈을 등록하는 이유는 스프링이 각 객체간 의존관계를 관리하도록 하는데에 큰 목적이 있습니다. 객체가 의존관계를 등록할 때는 스프링 컨테이너에서 해당하는 빈을 찾고, 그 빈과 의존성을 만듭니다.
  • @Bean
    • Configuration 클래스의 메서드 레벨에서 선언
    • @Bean이 선언된 객체는 스프링 컨테이너에서 관리되는 Bean으로 등록
  • @ConfigurationProperties
    • 인자에 prefix 속성 선언 가능
    • 여기에서 'prefix = spring.datasource.hikari' 선언
    • --> @PropertySource에 선언된 파일(application.properties)에서 'spring.datasource.hikari'로 시작되는 설정을 모두 불러옴
    • 해당 메서드에 매핑(바인딩)
  • hikariConfig
    • 히카리CP 객체 생성
    • 히카리CP : 커넥션 풀(Connection Pool) 라이브러리 중 하나
커넥션 풀(Connection Pool)

등장 배경
많은 애플리케이션에서 데이터베이스를 활용한다.
하지만, 데이터베이스에 연결하는 과정은 굉장히 비용이 많이 든다. 네트워크를 처리(TCP로 연결을 생성하고 종료)하고, 연결이 필요한 객체(SocketFactorv, Socket 등등)를 만들고 JVM GC에서 처리하는 과정은 굉장히 느리다. 데이터베이스와의 연결 비용을 줄이기 위해서 커넥션 풀 이라는 개념이 등장했다.

개념
쉽게 말해서, 커넥션을 미리 만들어주고 빌려주는 것 미리 DB에 연결된 객체를 만들어놓고, 이것들을 Connection Pool이라고 하는 컨테이너에 넣어둔다. DB와 연결 요청이 들어올 때, 커넥션 풀에서 연결 객체를 꺼내서 주고, 다 사용하면 재사용할 수 있게 다시 커넥션 풀에 반환한다.

connection pool은 미리 일정한 수의 데이터베이스 커넥션을 만들어두고, 애플리케이션이 데이터베이스에 연결을 요청할 때마다 이 커넥션을 제공해줍니다. 이렇게 함으로써, 매번 데이터베이스에 커넥션을 만들고 끊는 비용을 줄이고, 효율적으로 데이터베이스와의 통신을 처리할 수 있습니다.

예를 들어, 학생 정보를 조회하는 기능을 실행할 때, DataSource는 connection pool에서 사용 가능한 커넥션을 가져와서 데이터베이스에 쿼리를 실행하고, 결과를 가져옵니다. 이후에는 해당 커넥션을 다시 connection pool에 반환합니다. 이렇게 함으로써, 많은 요청이 동시에 발생해도 효율적으로 데이터베이스와의 연결을 관리할 수 있습니다.

Connection Pool을 사용한다면, 애플리케이션이 많은 요청을 처리할 때도 효율적으로 데이터베이스와의 연결을 관리할 수 있습니다. 연결풀을 사용하여 데이터베이스 연결을 미리 생성하고 재사용함으로써, 매번 연결을 만들고 끊는 비용을 줄이고, 성능을 향상시킬 수 있습니다.

종류

HikariCP, Tomcat pooling DataSource, Commons DBCP2, Oracle UCP 등 정말 다양한 커넥션 풀이 존재합니다.
다양한 이름들이 등장했지만, 각자 커넥션 풀을 다른 회사에서 다양한 방법으로 구현한 것에 불과하기 때문에, 커넥션 풀 라이브러리는 **공통된 인터페이스**를 제공합니다.
이 인터페이스로는 ConnectionPool, DataSource, ConnectionFactory 등이 있습니다.
잠시 DataSource, ConnectionFactory가 뭔지 간략하게 알아보고 넘어갑시다!

DataSource란?

개념
데이터베이스와 연결하는 데 필요한 정보를 제공하는 인터페이스입니다. 데이터 소스는 데이터베이스에 접근하기 위한 드라이버, URL, 사용자 이름, 암호 등을 포함합니다. 개발자는 데이터 소스를 사용하여 데이터베이스와의 연결을 설정하고, 커넥션 풀에서 커넥션을 가져올 수 있습니다.

✔️ [개인적 궁금증] 데이터베이스는 왜 외부 API 호출과 다르게 특별히 ‘연결’이라는 것을 할까?

문득 이런 생각이 들었습니다. 결국 웹 애플리케이션과 데이터베이스는 서로 다른 시스템인데, 그러면 외부 API를 호출하는 것과 데이터베이스의 API(주로 SQL문이 되겠죠)를 호출하는 것은 어떤 차이가 있는 걸까요?

호출하면 되는 것 뿐인데, 왜 특별히 연결이라는 작업을 거쳐야만 하는 걸까요?

아래와 같은 이유가 있을 것 같다는 결론을 내리게 되었습니다.

1. 데이터 보안

데이터베이스는 중요한 정보를 저장하는 곳이기 때문에 접근 제어와 보안이 필요합니다. 데이터베이스 연결을 통해 인증 및 권한 부여를 수행하여, 인가되지 않은 사용자가 데이터베이스에 접근하는 것을 방지할 수 있습니다.

2. 데이터의 일관성과 동시성 제어

여러 개의 사용자가 동시에 데이터베이스에 접근하는 경우, 데이터의 일관성과 동시성 제어가 필요합니다. 데이터베이스 연결을 통해 트랜잭션 관리와 락(lock)을 사용하여 데이터의 일관성을 유지하고, 동시에 여러 사용자가 데이터를 안전하게 수정할 수 있도록 합니다.

3. 효율적인 리소스 관리

데이터베이스 연결은 데이터베이스 서버와의 통신을 설정하는 과정입니다. 이를 통해 웹 애플리케이션은 데이터베이스와의 연결을 최적화하고, 리소스를 효율적으로 관리할 수 있습니다. 데이터베이스 연결을 효율적으로 관리함으로써, 불필요한 리소스 낭비를 방지하고 성능을 향상시킬 수 있습니다.

따라서 데이터베이스 연결은 데이터의 보안, 일관성, 동시성, 효율성을 유지하기 위해 필요한 과정입니다.

SpringBoot와 Connection Pool

앞서 설명한 다양한 종류의 Connection Pool 구현체 중에서, 스프링 부트는 성능과 동시성 면에서 우수한 성능을 보이는 HikariCP를 우선적으로 선택합니다. 만약 HikariCP를 사용할 수 없다면, Tomcat pooling DataSource, Commons DBCP2, Oracle UCP 등을 차례대로 사용하게 됩니다.

또한,
spring-boot-starter-jdbc

또는
spring-boot-starter-data-jpa

를 사용하는 경우에는 기본적으로 HikariCP에 대한 의존성이 자동으로 추가됩니다.

출처 : https://engineerinsight.tistory.com/238#%F0%9F%92%8B%20Connection%20Pool%EC%9D%B4%EB%9E%80%3F-1

 

[DB] Connection Pool: 개념, 등장 배경, Spring Boot에서 HikariCP 커넥션 풀 설정하기

💋 Connection Pool이란? ✔️ 등장 배경 많은 애플리케이션에서 데이터베이스를 활용한다. 하지만, 데이터베이스에 연결하는 과정은 굉장히 비용이 많이 든다. 네트워크를 처리(TCP로 연결을 생성

engineerinsight.tistory.com

  • dataSource
    • 데이터 소스 객체 생성
    • 순수 JDBC = SQL 실생 시마다 커넥션을 맺고 끊는 I/O 작업(리소스 증가!!)
      • 해결 : 커넥션 풀
    • 커넥션 풀
      • 커넥션 객체 생성 -> DB 접근 사용자에게 커넥션 주고 돌려 받기
      • dataSource : 커넥션 풀을 지원하기 위한 인터페이스
  • sqlSessionFactory
    • sqlSessionFactory 객체 생성
    • SqlSessionFactory
      • DB 커넥션, SQL 실행에 관한 모든 것을 갖는 객체
    • SqlSessionFactoryBean
      • FactoryBean 인터페이스 구현 객체
      • MyBatis, 스프링 연동 모듈
    • FactoryBean 객체
      • 데이터 소스 참조, XML Mapper(SQL 쿼리) 경로, 정보 경로 파일
  • sqlSession
    • sqlSession 객체 생성
    • sqlSessionTemplate
      • DB 커밋, 롤백 등 SQL 실행에 필요한 모든 메서드를 갖는 객체

6. JUnit으로 단위 테스트해 보기

1) 소스코드 작성하기

src/test/java/BoardApplicationTests

package com.study;

import org.apache.ibatis.session.SqlSessionFactory;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;

@SpringBootTest
class BoardApplicationTests {

    @Autowired
    private ApplicationContext context;

    @Autowired
    private SqlSessionFactory sessionFactory;

    @Test
    void contextLoads() {
    }

    @Test
    public void testByApplicationContext() {
        try {
            System.out.println("=========================");
            System.out.println(context.getBean("sqlSessionFactory"));
            //context : 스프링컨테이너 중 하나인 ApplicationContext의 객체
            System.out.println("=========================");

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Test
    public void testBySqlSessionFactory() {
        try {
            System.out.println("=========================");
            System.out.println(sessionFactory.toString());
            System.out.println("=========================");

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

 

    @Test
    public void testByApplicationContext() {
        try {
            System.out.println("=========================");
            System.out.println(context.getBean("sqlSessionFactory"));            
            //context : 스프링컨테이너 중 하나인 ApplicationContext의 객체

            System.out.println("=========================");

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

 


 

오늘 내용이 길었습니다.

 

DB와 커넥션 부분은 처음 접하는 내용이라 개념을 익히는 데에 시간이 많이 필요했습니다.

아직 완벽하게 이해한 것은 아니라도

어느 정도 DB 커넥션의 프로세스, 흐름을 파악하는 데 도움이 되었다고 생각합니다.

 

그럼 이만!