저희 프로젝트에서 서버는 SpringBoot로 열기! 가 결정되었습니다....
그래서 다시 Java 공부를 해야 하는 상황에서 강사님의 자료가 있어서 그 자료로 실습을 하면서 익혀보고자 합니다.
Maven으로 빌드하고자 했던 파일은 Gradle로 변경했습니다.
(강사님 : 이건 제가 현직에 있을 때나 쓰던 건데요...? 새로운 게 나오면 새로운 걸 써야죠!)
Gradle
: 오픈소스 빌드 자동화 툴로, 거의 모든 타입의 소프트웨어를 빌드할 수 있음
○ 특징
● High Performance : 실행시켜야 하는 task만 실행하고 build cache를 통해 이전 실행 task output을 재사용함
● JVM foundation : JVM에서 실행되기 때문에 JDK를 설치해야 함
● Convetions : Maven으로부터 의존 라이브러리 관리 기능을 차용
● Extensibility : Gradle을 고유의 task 타입을 제공하거나 모델을 빌드할 수 있음
○ 구성(In Spring)
├─ gradle
│ └─ wrapper
│ ├─ gradle-wrapper.jar
│ └─ gradle-wrapper.properties
├─ gradlew
├─ gradlew.bat
├─ build.gradle
└─ settings.gradle
○ Gradle 라이브러리 의존성 관리
● build.gradle
- 라이브러리 의존성 설정은 build.gradle(스크립트 파일)에 작성 가능
- Dependency Configuration
* Implementation : 구현할 때만 사용
* complieOnly : 컴파일 때만 사용
* runtimeOnly : 런타임 때만 사용
* testImplementation : 테스트에서만 사용
Filter
: 디스패처서블릿에 요청이 전달되기 전/후에 url패턴에 맞는 모든 요청에 대해 부가작업을 처리할 수 있는 기능 제공
단, Request, Response는 조작 가능하지만 인터셉터는 조작 불가
FilterChain(필터 체인)을 통해 여러 필터가 연쇄적으로 동작하게 가능(오른쪽 사진)
○ 사용법
● 요청/응답 로깅 : 요청 및 응답 내용 기록 & 모니터링 용도로 사용
● 인증 및 권한 부여 : 요청에 대한 인증 및 권한 부여 작업
● 데이터 변환 : 요청 데이터 | 응답 데이터 변환 or 형식 조작 가능
● 캐싱 : 응답을 캐시 하여 성능 향상 가능(exception 처리 가능)
○ 주요 메서드
● init() : 필터 인스턴스 초기화 시 실행되는 메서드
● doFilter() : 클라이언트의 요청/응답 처리 시 실행되는 메서드
● destory() : 필터 인스턴스가 제거될 때 실행되는 메서드
Interceptor
: 스프링 프레임워크에서 제공하는 기능
웹 앱의 요청 처리 과정에서 컨트롤러 호출 전후에 추가적인 작업을 수행할 수 있도록 도와줌
○ Spring MVC Request Lifecycle
○ Interceptor를 사용하면 가능한 작업
● 요청 전/후에 공통적으로 처리해야 할 작업을 수행 가능
e.g. 인증 및 권한 검사, 세션 관리 등
● 컨트롤러 실행 전/후에 부가적인 작업을 수행 가능
e.g. 로깅, 성능 측정, 트랜잭션 관리 등
● 컨트롤러의 결과를 가공하거나 추가 데이터 주입 가능
○ 주요 메서드
● perHandle()
- 컨트롤러 실행 전 호출되는 메서드
- 주로 요청 전에 수행해야 하는 사전 처리 작업 구현
● postHandle()
- 컨트롤러 실행 후 뷰가 렌더링 되기 전에 호출되는 메서드
- 컨트롤러가 실행된 이후 추가적인 처리 작업 수행 가능
● afterCompletion()
- 뷰가 렌더링 된 후에 호출되는 메서드
- 요청의 완료 후에 처리해야 하는 작업을 구현 가능
○ Filter와 차이점
● Filter
- 서블릿 컨테이너에서 작동 So, 웹 앱 전체에 대해 적용
- 모든 요청과 응답에 대해 적용
● Interceptor
- Spring MVC 프레임워크에서 작동 So, Spring MVC 콘텍스트에만 적용
- 요청이 MVC 컨트롤러로 라우팅 될 때만 Interceptor가 적용
SpringDBJPA
MySQL 설치
1. dbeaver 설치
2. Docker 설치
2024.07.18 - [Networks/MySQL&DB] - SK networks AI Camp - MySQL & DBeaver설치
3. mysql 설치 폴더 생성
mkdir ./mysql
mkdir ./mysql/data
4../mysql/docker-compose.yml 파일 생성
version: "2"
services:
vacation-db:
image: mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: "root1234"
MYSQL_DATABASE: "examplesdb"
MYSQL_USER: "urstory"
MYSQL_PASSWORD: "u1234"
command:
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_unicode_ci
volumes:
- ./database/init/:/docker-entrypoint-initdb.d/
- ./database/datadir/:/var/lib/mysql
platform: linux/x86_64
ports:
- 3306:3306
MySQL 실행
cd ./mysql # docker-compose.yml이 있는 폴더로 이동
docker-compose up -d # mysql 생성 및 실행
docker ps # 생성된 mysql 확인
Spring Boot에 MySQL 적용
●./src/resources/application.yml 생성(application.properties 파일 삭제!!!!!)
* 강사님 왈 : yml 파일로 주로 사용하는 경향이 있다.
# Spring Data Source 설정
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/examplesdb?userSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Seoul
username: urstory
password: u1234
driver-class-name: com.mysql.cj.jdbc.Driver
DTO, DAO, Repository, Entity
○ Spring Boot 서비스 구조
○ Entity(Domain)
● DB에 쓰일 칼럼, 여러 엔티티 간의 연관관계 정의
● DB의 테이블을 하나의 엔티티로 생각
● 이 클래스의 필드는 각 테이블 내부의 Column을 의미
○ Repository
● Entity에 의해 생성된 DB에 접근하는 메서드를 사용하기 위한 인터페이스
● Service와 DB를 연결하는 고리 역할 수행
● DB에 적용하고자 하는 CRUD 정의
○ DAO(Data Access Object)
● DB에 접근하는 객체
● Service가 DB에 연결할 수 있게 해주는 역할
● DB를 사용해 데이터 조회, 조작하는 기능 담당
○ DTO(Data Transfer Object; VO(Value Object))
● 계층 간 데이터 교환을 위한 객체
● VO의 경우 Read Only 개념을 가지고 있음
ORM(Object Relational Mapping)
: 애플리케이션 객체와 관계형 DB의 데이터를 자동으로 매핑해 주는 것
java의 데이터 클래스와 RDB의 테이블 매핑
객체지향 프로그래밍과 관계형 DB의 차이로 발생하는 제약사항을 해결해 주는 역할 수행
e.g. JPA
○ 장점
● SQL 쿼리가 아닌 직관적인 코드로 데이터 조작 가능(So, 비즈니스 로직에 집중 가능)
● 재사용 및 유지보수 편리
- ORM은 독립적으로 작성 So, 재사용 가능
- 매핑정보를 명확히 설계 So, 따로 DB 볼 필요 없음
● DBMS에 대한 종속성이 줄어듦
- DBMS 교체 작업을 비교적 적은 리스크로 가능
○ 단점
● 복잡성이 커질 경우 ORM만으로 구현이 어려움(직접 쿼리 구현하지 않아 복잡한 설계 불가)
● 잘못 구현하면 속도 저하(대형 쿼리의 경우 별도의 튜닝 필요)
○ JPA(Java persistence API)
● JPA는 ORM과 관련된 인터페이스 모음
● ORM이 큰 개념이라고 하면, JPA는 더 구체화시킨 스펙을 포함
○ Hibernate
● ORM Framework 中 1
● JPA 구현체 中 1, 현재 가장 많이 사용
○ Spring Data JPA
● Spring Framework에서 JPA를 편리하게 사용할 수 있게 지원하는 라이브러리
● CRUD 처리용 인터페이스 제공
* CRUD : Create, Read, Update, Delete의 약자로, 저장된 데이터에 대한 작업 방법을 의미
● Repository 개발 시 인터페이스만 작성하면 구현 객체를 동적으로 생성해 주입
● 데이터 접근 계층 개발 시 인터페이스만 작성해도 괜찮음
○ JPA Query Method
: 스프링 데이터 JPA는 메서드 이름으로 쿼리 생성하는 쿼리 메서드 기능 제공
● 쿼리 메서드 : 메서드 이름을 분석해 JPQL 쿼리 실행
● 쿼리메서드를 활용하면 쉽게 쿼리문 작성 가능
● Select
User findByEmail(String email);
User getByEmail(String email);
User readByEmail(String email);
User queryByEmail(String email);
User searchByEmail(String email);
User streamByEmail(String email);
User findUserByEmail(String email);
● And, or
List<User> findByNameAndEmail(String name, String email);
List<User> findByNameOrEmail(String name, String email);
● is(Not) Empty, is(Not) Null
List<User> findByIdIsNotNull(); // Id값에 Null값이 없는지?
List<User> findByAddressIsNotEmpty();
● in / StringWith / EndingWith / Contains
- findByNameIn : 입력된 이름 목록 중 하나라도 해당하는 이름을 가진 사용자 리스트 조회
- findByNameStartingWith : 입력된 문자열로 시작하는 이름을 가진 사용자 리스트 조회
- findByNameEndingWith : 끝나는 이름
- findByNameContains : 포함된 이름
- findByNameLike : 패턴이 일치하는 이름 가진 사용자
List<User> findByNameIn(List<String> name);
List<User> findByNameStartingWith(String name);
List<User> findByNameEndingWith(String name);
List<User> findByNameContains(String name);
List<User> findByNameLike(String name);
●Is / Equals / Sorting
Set<User> findUserByNameIs(String name);
Set<User> findUserByName(String name);
Set<User> findUserByNameEquals(String name);
List<User> findTop1ByNameOrderByIdDesc(String name);
// Id로 내림차순으로 정렬 후 입력 name과 같은 것의 맨 위의 있는 값을 뽑아온다.
List<User> findFirst2ByNameOrderByIdDescEmailAsc(String name);
// 여러개의 조건으로 find하는 경우는 And를 사용하였으나 정렬 조건으로 여러개의 값을 사용하는 경우는 And를 사용하지 않고 조건을 이어서 붙인다.
List<User> findFirstByName(String name, Sort sort);
MyBatis
: 객체 지향 언어인 자바의 RDBMS 프로그래밍을 좀 더 쉽게 할 수 있게 도와주는 개발 프레임 워크
JDBC를 통해 DB에 액세스 하는 작업을 캡슐화
일반 SQL 쿼리, 저장 프로 시저 및 고급 매핑 지원
모든 JDBC 코드 및 매개 변수의 중복작업을 제거
- MyBatis에서 프로그램에 있는 SQL 쿼리를 한 구성파일에 구성하여 프로그램 코드와 SQL코드를 분리할 수 있는 장점
○ Mybatics 설정
[블로그 방법]
1. logback-spring.xml 추가(resources 안에 작성)
- appender : 전달받은 로그 출력할 곳 결정(콘솔 | 파일 저장 | DB 저장)
- encoder : appender에 포함되어 출력할 로그 형식 지정
- logger : 로그를 출력하는 요소
level 속성을 통해 출력할 로그 레벨을 조절하여 appender에 전달
첫 번째 logger에서 com.study = java 디렉터리의 java 패키지 경로를 의미
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true">
<!-- Appenders -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<charset>UTF-8</charset>
<Pattern>%d %5p [%c] %m%n</Pattern>
</encoder>
</appender>
<appender name="console-infolog" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<charset>UTF-8</charset>
<Pattern>%d %5p %m%n</Pattern>
</encoder>
</appender>
<!-- Logger -->
<logger name="com.study" level="DEBUG" appender-ref="console" />
<logger name="jdbc.sqlonly" level="INFO" appender-ref="console-infolog" />
<logger name="jdbc.resultsettable" level="INFO" appender-ref="console-infolog" />
<!-- Root Logger -->
<root level="off">
<appender-ref ref="console" />
</root>
</configuration>
log
○ 로그 레벨
● fatal / error : (심각한) 에러
● warn : 실행은 문제없지만 나중에 시스템 에러 원인이 될 수 있음
● trace : 디버그 레벨이 너무 광범위한 것을 해결하기 위해 좀 더 상세한 이벤트를 나타냄
○ 로그 타입
● sqlonly : SQL을 로그로 남김(Prepared Statement와 관련된 파라미터 자동으로 변경되어 SQL 출력)
● sqltiming : SQL과 SQL 실행시간 출력
● audit : ResultSet을 제외한 모든 JDBC 호출 정보 출력(사용 권장 X)
● resultset : ResultSet을 포함한 모든 JDBC 호출 정보 출력
● resultsettable : SQL 조회 결과를 테이블 형태로 출력
● connection : Connection의 연결과 종료에 관련된 로그를 출력
2. Log4 JDBC 라이브러리 추가하기
: SQL 쿼리를 깔끔하게 정렬된 상태로 출력하고 추가적인 정보를 제공받을 수 있도록 추가
build.gradle의 dependencise에 Log4 JDBC 라이브러리 추가하고 다시 로드
* implementation은 꼭 implementation끼리 뭉쳐있어야 함
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.3.0'
implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect' /* Thymeleaf Layout */
implementation 'org.bgee.log4jdbc-log4j2:log4jdbc-log4j2-jdbc4.1:1.16' /* Log4JDBC */
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
runtimeOnly 'org.mariadb.jdbc:mariadb-java-client'
annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
3.log4 jdbc.log4j2.properties 추가(resources 디렉터리 안)
# log4jdbc spy의 로그 이벤트를 slf4j를 통해 처리
log4jdbc.spylogdelegator.name=net.sf.log4jdbc.log.slf4j.Slf4jSpyLogDelegator
# 로그를 표시할 줄의 제한, 0은 무제한
log4jdbc.dump.sql.maxlinelength=0
4. jdbc-url과 driver-class-name 변경
: application.properties의 데이터 소스 설정을 변경(DB 정보는 환경에 맞게 설정)
spring.datasource.hikari.driver-class-name=net.sf.log4jdbc.sql.jdbcapi.DriverSpy
spring.datasource.hikari.jdbc-url=jdbc:log4jdbc:mariadb://localhost:3306/board?serverTimezone=Asia/Seoul&useUnicode=true&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true
spring.datasource.hikari.username=username
spring.datasource.hikari.password=password
spring.datasource.hikari.connection-test-query=SELECT NOW() FROM dual
[강사님 방법]
1. build.gradle
dependencies {
...
implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:3.0.2'
implementation 'org.bgee.log4jdbc-log4j2:log4jdbc-log4j2-jdbc4.1:1.16'
...
testImplementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter-test:3.0.2'
...
}
2. log4 jdbc.log4j2.properties
# log4jdbc spy의 로그 이벤트를 slf4j를 통해 처리한다.
log4jdbc.spylogdelegator.name=net.sf.log4jdbc.log.slf4j.Slf4jSpyLogDelegator
# 로그를 표시할 줄의 제한, 0은 무제한
log4jdbc.dump.sql.maxlinelength=0
3. application.yml
# Spring Boot 설정!!!
spring:
# Database(MySQL) 설정!!
datasource:
username: urstory
password: u1234
# url: jdbc:mysql://127.0.0.1:3306/examplesdb?userSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Seoul
# driver-class-name: com.mysql.jdbc.Driver
# log4jdbc 적용!!
url: jdbc:log4jdbc:mysql://127.0.0.1:3306/examplesdb?userSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Seoul
driver-class-name: net.sf.log4jdbc.sql.jdbcapi.DriverSpy
hikari:
connection-test-query: SELECT NOW() FROM dual
# Mybatis 설정!!!
mybatis:
# default Package location - resultType의 Alias를 지정합니다.
type-aliases-package: com.example.basic.model.entity
# mapper location - 바라 볼 xml 파일을 지정합니다.
mapper-locations: classpath:mapper/**/*.xml
# column name to camel case - 반환 받는 컬럼명을 CamelCase로 받는 설정을 합니다.
configuration:
map-underscore-to-camel-case: true
4. logback-spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true">
<!-- Appenders -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<charset>UTF-8</charset>
<Pattern>%d %5p [%c] %m%n</Pattern>
</encoder>
</appender>
<appender name="console-infolog" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<charset>UTF-8</charset>
<Pattern >%d %5p %m%n</Pattern>
</encoder>
</appender>
<!-- Logger -->
<logger name="com.study" level="DEBUG" appender-ref="console" />
<logger name="jdbc.sqlonly" level="INFO" appender-ref="console-infolog" />
<logger name="jdbc.resultsettable" level="INFO" appender-ref="console-infolog" />
<!-- Root Logger -->
<root level="off">
<appender-ref ref="console" />
</root>
</configuration>
Mybatis 동적 SQL
○ if
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG WHERE state = ‘ACTIVE’
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</select>
○ choose / when / otherwise
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG WHERE state = ‘ACTIVE’
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</select>
○ where
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG
<where>
<if test="state != null">
state = #{state}
</if>
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</where>
</select>
○ set
<update id="updateAuthorIfNecessary">
update Author
<set>
<if test="username != null">username=#{username},</if>
<if test="password != null">password=#{password},</if>
<if test="email != null">email=#{email},</if>
<if test="bio != null">bio=#{bio}</if>
</set>
where id=#{id}
</update>
○ foreach
<select id="selectPostIn" resultType="domain.blog.Post">
SELECT *
FROM POST P
<where>
<foreach item="item" index="index" collection="list"
open="ID in (" separator="," close=")" nullable="true">
#{item}
</foreach>
</where>
</select>
'Networks > SpringBoot' 카테고리의 다른 글
SK networks AI Camp - SpringBoot(3) (0) | 2024.09.09 |
---|---|
SK networks AI Camp - SpringBoot(2) (4) | 2024.09.06 |