앞서 우리는 ec2 인스턴스를 만들어 보고 ssh를 이용하여 서버에 접근하는 방법까지 익혔습니다.

이번 포스트에서는 프로젝트를 해당 서버를 통해서 배포하는 과정을 알아보겠습니다.

해당 과정은 CI/CD가 아님을 미리 알려드립니다.

 

 

1️⃣설정

먼저 우리가 생성했던 서버에서 먼저 설정해야 될 사항들을 설정하겠습니다.

 

-자바11 설치

 

스프링 프로젝트를 띄우기 위해서 우리는 자바11을 설치 하겠습니다. 

 

설치하기 앞서 yum list java*  로 java 설치 가능 리스트 검색을 해줍니다.

java-11-amazon-corretto.x86_64 가 ec2에서 설치할 수 있는 java11 jdk임을 확인할수 있습니다.

* Amazon Corretto 란 무료로 사용할 수 있는 Open Java Development Kit (OpenJDK) 의 프로덕션용 멀티플랫폼 배포판입니다.

 

sudo yum install java-11-amazon-corretto.x86_64 로 설치를 해줍니다. 자바를 설치해줍니다.

 

java -version을 통해서 정상적으로 설치 되었는지 확인해주세요

 

 

 

-타임존 변경

 

date 명령을 치면 현재 한국 시간과 동떨어져있음을 확인할수 있습니다.

sudo rm /etc/localtime

sudo ln -s /usr /share/zoneinfo/Asia/Seoul /etc/localtime

두명령어를 통해서 기존의 시간을 지우고 한국 시간으로 변경해줍니다.

date 명령으로 잘 변경 되어있음을 확인할 수 있습니다.

 

-Hostname 변경

 

ip만으로 어떤 서비스의 서버인지 확인하기 위해서 Hostname을 변경하겠습니다.

sudo vi /etc/hostname 입력 후 원하는 hostname으로 수정해줍니다.

cat /etc/hostname 입력하면 변경한 hostname이 변경된 것을 확인할 수 있습니다.

hostname 명령어를 통해 현재 설정된 hostname을 확인합니다.

hostnamectl set-hostname <변경할 hostname>을 통하여 hostname을 변경해 줍니다.

 

putty를 종료하고 시작하면 hostname이 잘 변경된 것을 확인할수 있습니다.

 

 

2️⃣배포

 

-ec2에 프로젝트 Clone받기

 

먼저 배포하기 전에 프로젝트를 깃을 통해 clone해줍니다.

그렇기 위해서 sudo yum install git 을 통해서 git 을 설치합니다.

 

mkdir ~/app && mkdir ~/app/webservice를 이용하여 git clone으로 프로젝트를 저장할 디렉토리를 만듭니다.

cd ~/app/webservice/ 를 통해서 생성 디렉토리로 이동합니다.

 

git clone <깃허브 repository https 주소> 를 입력하여 프로젝트를 clone해주세요.

 

 

cd 프로젝트명

ll

해당 명령어 들로 잘 복사되었는지 확인합니다.

 

 

 

./gradlew test 로 빌드 테스트를 해줍니다. permission denied가 뜰수 있는데 그럴땐 

chmod + x ./gradlew 을 이용해서 실행 권한을 주고 다시 해보세요.

빌드 테스트 성공

 

 

 

 

-배포 스크립트 만들기

 

매번 배포할 때마다 개발자가 하나하나 이런 명령어를 통해 배포를 진행하는 것은 큰 불편함을 야기합니다.

따라서 쉘 스크립트를 통해서 스크립트만 실행하여 앞의 과정들을 진행해보겠습니다.

 

vim ~/app/webservice/deploy.sh 로 배포를 위한 쉘 스크립트를 먼저 만들어보겠습니다.

#!/bin/bash

REPOSITORY=/home/ec2-user/app/webservice
PROJECT_NAME=dockertest

cd $REPOSITORY/$PROJECT_NAME/

echo "> Git Pull"

git pull

echo "> 프로젝트 build 시작"

./gradlew build

echo "> webservice 디렉토리로 이동"

cd $REPOSITORY

echo "> build파일 복사"

cp $REPOSITORY/$PROJECT_NAME//build/libs/*.jar $REPOSITORY/

echo "> 현재 구동중인 애플리케이션 pid 확인"

CURRENT_PID=$(pgrep -f ${PROJECT_NAME}.*.jar)

echo "현재 구동 중인 애플리케이션 pid: $CURRENT_PID"

if [ -z "$CURRENT_PID" ]; then
        echo "> 현재 구동중인 애플리케이션이 없으므로 종료하지 않습니다."
else
        echo "> kill -15 $CURRENT_PID"
        kill -15 $CURRENT_PID
        sleep 5
fi

echo "> 새 애플리케이션 배포"

JAR_NAME=$(ls -tr $REPOSITORY/ | grep '.*[.]jar' | grep -v plain  | tail -n 1)

echo "> JAR Name: $JAR_NAME"

nohup java -jar $REPOSITORY/$JAR_NAME 2>&1 &

 

 

 

 

이제 생성한 스크립트에 실행 권한을 추가합니다.

chmod +x ./deploy.sh

 

 

 

./deploy.sh 로 스크립트를 실행해주세요.

vim nohup.out 을 사용해서 nohup.out파일을 열어서 로그를 확인해 봅니다.

nohup.out은 실행되는 애플리케이션에서 출력되는 모든 내용을 가지고 있습니다.

 

인스턴스의 퍼블릭 IPv4 DNS로 외부에서 접속하여 성공적으로 배포됨을 확인하였습니다. 

👀참조

해당 포스팅은 이동욱님의 "스프링 부트와 AWS로 혼자 구현하는 웹 서비스" 서적을 인용하였습니다.
스프링 부트와 AWS로 혼자 구현하는 웹 서비스

글을 쓰기 앞서 해당 과정은 AWS에서 무료로 제공하는 프리티어 플랜에서의 서버구축입니다. 

 

 

 

 

 

1️⃣EC2인스턴스 생성

EC2(Elastic Compute Cloud)는 AWS에서 제공하는 성능, 용량 등을 유동적으로 사용할 수있는 서버입니다.

 

-AWS홈페이지에서 EC2를 검색해줍니다.

-인스턴스 시작을 눌러서 인스턴스 생성 설정을 해줍니다.

 

-인스턴스 시작 설정을 해주겠습니다.

먼저 AMI(Amazon Machine Image)를 선택해줍니다. 인스턴스라는 가상 머신에 운영체제 등을 설치할 수 있게 구워 넣은 이미지라고 생각하면 됩니다.

아마존 리눅스 2를 사용해보겠습니다.

 

-인스턴스 유형을 선택합니다. 프리터어로 표기된 t2.micro를 사용하겠습니다.

-스토리지 선택을 합니다. 스토리지는 서버의 디스크를 이야기합니다. 쉽게 서버의 보조기억장치라고 생각하시면 됩니다.

기본값은 8GB인데 프리티어사용시 30GB까지 가능하니 30GB로 늘려줍니다.

 

-태그를 등록해줍니다. spring-test로 지정해봤습니다. 쉽게 인스턴스의 이름이라고 생각해주세요.

 

-보안그룹설정입니다. 보안 그룹은 방화벽을 이야기하는데, '서버로 80포트 외에는 허용하지 않는다' 라는 역활을 하는 방화벽이 aws에서는 보안 그룹으로 사용됩니다. 

 

네트워크설정 > 편집  > 보안 그룹 이름에 유의미한 이름으로 변경해주고 아래와 같이 보안 그룹 규칙을 설정해줍니다.

SSH이면서 포트 범위가 22인 경우 AWS EC2에  터미널로 원격 접속을 할때를 말합니다.
어차피 pem키가 없으면 접속이 안 되니 전체 오픈(0.0.0.0/0,::/0)하는 경우가 있으나, 이렇게 되면 깃허브 등에 실수로 pem 키가 노출되면 가상화폐채굴서버가 되는 등 많은 금전피해를 줄수 있으니 조심하여 주세요.
pem 키 관리와 지정된 ip(ex 본인 집 ip) 에서만ssh 접속이 가능하도록 구성하는 것이 안전합니다.
장소가 바뀔때는 해당 장소의 ip를 다시 ssh규칙에 추가하는 방법이 안전합니다.

 

-pem키 할당하기.

 

인스턴스로 접근하기 위해서는 pem키(비밀키)가 필요합니다.

인스턴스는 지정된 pem키와 매칭되는 공개키를 가지고 있어서 해당 pem키 이외에는 접근을 허용하지 않습니다.

따라서 절대 유출되면 안됩니다.

기존의 pem키를 가지고 있으면 사용하시면 되고 없으시면 생성해주세요.

 

spring-test pem키를 생성해 보겠습니다. 생성과 동시에 pem키가 다운 받아집니다.

 

-인스턴스 생성하기

 

인스턴스 시작을 눌러 인스턴스를 생성해 주세요.

 

 

-성공적을 인스턴스를 생성된것을 확인할 수 있습니다.

 

2️⃣탄력적 ip 할당

인스턴스를 중지하고 다시 시작할 때면 새 ip가 매번 할당이 됩니다. 다시말해서 요금을 아끼기 위해 인스턴스를 중지하고 다시 시작하면 ip가 새로 할당되는 것을 볼수있죠

우리는 그래서 고정ip 즉 탄력적 ip를 생성 하고 할당해 주겠습니다.

 

-탄력적ip 발급받기

 

ec2 인스턴스 페이지 왼쪽 카테고리 > 탄력적 IP > 탄력적 IP 주소 할당 > 할당

 

 

 

-탄력적 ip와 ec2주소의 연결

 

탄력적 ip는 생성하고 ec2 서버에 연결하지 않으면 비용이 청구됩니다. 따라서 생성후 바로 ec2에 연결하여야 하고 사용할 인스턴스가 없으면 탄력적 ip는 꼭 삭제해주세요.

 

탄력적 ip에 인스턴스id가 연결 되지않은 것을 볼 수 있습니다.

작업  > 주소 연결 > 인스턴스 와 프라이빗 IP입력 > 연결

 

인스턴스 정보에 탄력적 IP가 잘 연결되었음을 확인할 수 있습니다.

 

 

3️⃣EC2 서버에 접속

ec2로 접속을 해보겠습니다.

해당 방법은 윈도우 유저가 ssh를 통해서 서버에 접속하는 과정임을 알려드립니다.

먼저 윈도우에서는 ssh접속하기위해 putty 와 puttygen프로그램이 필요합니다.

 

-putty.exe와 puttygen.exe설치

putty 공식 홈페이지에 가셔서 download를 하시면 됩니다.

https://www.putty.org/

 

 

-puttygen을 통해서 pem키를 ppk파일로 변환하기

putty는 pem키를 직접 사용이 안되며 ppk파일로 사용해야 합니다.

따라서 아까 pem키를 발급받으면서 download됐던 pem 키를 ppk파일로 변경해주겠습니다. 

 

import key > pem키를 선택하여 변환해줍니다.

 

-putty로 실행하기

 

각 항목을 입력해줍니다.

HostName: <username>@<public_ip>

Amazon Linux는 ec2-user가 username입니다. public_ip에는 인스턴스에 할당시켜준 탄력적 ip 주소를 등록해줍니다.

Port: ssh의 접속포트인 22를 등록해줍니다.

Connect type: SSH로 선택해주세요.

 

왼쪽 카테고리> Connection > SSH > Auth > Browse 에서

puttygen으로 만들어준 ppk 파일을 선택해서 불러옵니다.

 

-다시 Session 탭으로 돌아와서 Saved Session에 현재 설절들을 저장할 이름을 등록 후 save를 하여 저장해주고, open버튼을 클릭하여 서버에 접속을 해줍니다.

 

-SSH 접속이 성공 하였습니다. 

 

 

📔참조

해당 포스팅은 이동욱님의 "스프링 부트와 AWS로 혼자 구현하는 웹 서비스" 서적을 인용하였습니다.
스프링 부트와 AWS로 혼자 구현하는 웹 서비스

 

우리가 도커를 이해해야 하는 이유는 호스팅 서비스를 이용하기 위해서는 특정한 서버 환경에 배포를 해야하기 때문에 그에 따른 설정을 맞춰줘야 됩니다.
하지만 매번 다른 웹서비스를 이용할 때 마다 설정을 해야하며, 서버마다 각각의 설정을해줘야 하여 많은 자원 남이가 있습니다.
이런 다른 환경에서의 작업을 규격화하여 정의를 하기 위해 도커를 사용합니다.

 


#️⃣ 도커 실행 순서

 

1. spring boot 개발

2. jar 파일 생성

3. dockerfile 만들기

4. docker image생성

5. docker container실행

 

먼저 실습을 하기앞서 도커가 설치가 되어있어야 합니다.

 

도커 설치법

 

 


 

1️⃣Spring Boot 개발

해당 포스팅은 도커를 중점으로 하는 내용이기 때문에 spring 프로젝트는 간단히 "Hello World"로 RestContoller를 이용해서 만들겠습니다. 

@RestController
public class IndexController {
    @GetMapping("/")
    public String index() {
        return "Hello World";
    }
}

해당 코드는 깃허브에 올려두었습니다.(깃허브 바로가기)

 

 


 

2️⃣ jar파일 생성

jar파일은 여러 개의 자바 클래스 파일과 클래스들이 이용하는 관련 리소스 등 메타데이터를 하나의 파일로 모아서 자바 플랫폼에 응용 소프트 웨어나 라이브러리를 배포하기 위한 소프트웨어 패키티 타일 포맷입니다.
쉽게 일종의 자바 프로젝트 압축파일로 생각하시면 됩니다.

 

 

 

터미널에서 ./gradlew clean build로 기존의 빌드된 파일을 전부 지우고 다시 빌드를 해줍니다.

프로젝트 폴더>build>libs에 jar파일이 생성된것을 확인할수 있습니다.

 


 

 

 3️⃣도커 파일 만들기

프로젝트 최상단에 Dockerfile을 생성해 주었습니다.

간단하게 도커파일을 작성해 보았습니다. 

해당파일은 깃허브에 올려두었습니다. (깃허브 바로가기)

 

FROM을 사용해서 Base Container image를 지정합니다.

우리는 굳이 jdk를 사용할 필요가 없어서 jre를 이용하였습니다.

(jre는 자바 프로그램을 실행하는데 필요한 도구이고 jdk는 실행뿐 아니라 개발할수잇는 도구입니다. 즉, jdk안에 jre가 포함되어 있습니다. )

COPY는 COPY <복사할 파일 경로> <이미지에서 파일이 위치할 경로>로 호스트(local)에서 이미지에 파일을 추가 해줍니다.

ENTRYPOINT는 컨테이너를 시작할 때 실행할 명령어 입니다.

 


 

 

4️⃣docker image생성

docker build -t dockertestimg . 를 사용해서 dockertestimg라는 이름의 도커이미지를 생성합니다.

docker build -t <타겟이름(생성될 이미지이름)> <도커파일위치>

docker images를 통해 dockertestimg라는 도커이미지가 생성된 것을 확인할수 있습니다. 

 

 


 

 

 

5️⃣docker container실행

docker run <도커이미지명> 을 통해서 도커이미지를 container를 실행시킬수 있습니다.

하지만 여기서 8080포트로 진입을 할수가없습니다.

이미지 기반으로만든 컨테이너 안에서의 8080포트이지 localhost의 8080포트를 사용하는것이 아니기 때문입니다.

따라서 포트맵핑이 필요합니다.

docker run -p 8080:8080 dockertestimg로 컨테이너의 포트를 호스트 시스템의 포트에 바인딩해줍니다.

docker run -p <호스트 시스템의 포트>:<컨테이너 안의 포트> <도커이미지명>

잘 실행되는것을 확인할수있습니다.

 

불편한점은 컨테이너 자체가 foreground로 뜨기 때문에 콘솔을 다시쓸수가 없습니다.

-d (detatched)옵션을 이용해서 background로 띄워서 docker console를 다시 쓸수있게 해주겠습니다. 

docker ps 를 이용해서 현재 도커컨테이너가 잘 실행되는 것을 확인 할수 있습니다. 

 

 

 

 

 

 

 

👀참조

https://hanhyx.tistory.com/27
 

도커(Docker)의 개요 및 장점 그리고 도커를 쓰는 이유

안녕하세요 오늘 포스팅에 앞서 도커 공식 서비스의 메인 로고를 보여드렸습니다. 마치 고래같은 범선 (이미고래잖아..) 이 컨테이너를 운반하는 모습처럼 생겼네요. ( 내 최애...귀염귀염 ) 우선

hanhyx.tistory.com

https://da2uns2.tistory.com/m/entry/Docker-%EB%8F%84%EC%BB%A4%EC%97%90-Spring-Boot-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0
 

[Docker] 도커에 Spring Boot 구축하기

목차 1. Spring Boot 코드 작성 2. jar 파일 생성 3. Dockerfile 만들기 4. 컨테이너 실행하기 1. Spring Boot 코드 작성 간단한 Hello World 코드를 구현했다. 소스코드는 다음과 같다. package hello.hellosprin..

da2uns2.tistory.com

백기선님 유튜브

 

 

OAuth2.0

 

API를 이용해서 다른 서비스에 있는 정보를 자신의 애플리케이션으로 가져와서 가공하고 무언가를 행하는 것은 중요한 기술입니다.

 

다른 서비스의 정보를 가져오고, 그 정보가 특정 사용자의 개인적인 정보일 때는 그 사용자의 허락과 가지고 오려는 서비스의 정해진 절차를 따라야 정보를 가져올 수 있습니다.
그때 사용할 수 있는 기술이 oauth입니다.

 

아주 옛날에는 Resource Owner에게 본인의 Resource Server의 id, password를 받아와서(이를테면 개인의 구글 아이디와 비밀번호), 직접 Resource Server에 접속하는 방법으로 사용하였으나 보안에 매우 취약해서 oauth 방식이 개발되었습니다.

 

 

동작 매커니즘


oauth를 이용한 애플리케이션을 운영하기 위해서 우리 애플리케이션을 구글에 먼저 사용등록을 합니다. 구글은 서비스를 식별할 수 있도록 client id와 client secret 두 개의 값을 발급해 줍니다. 우리는 이제 이것을 이용해서 애플리케이션을 운영해 나갑니다. 특히 client secret은 외부에 노출하면 안 됩니다.

이제 애플리케이션이 리소스를  사용하기 위해  사용자에게 허락을 받아야 합니다. 사용자는 애플리케이션을 이용해서 Resource server에 접속해 인증을 해줍니다.

 

사용자의 승인으로 resource server는 애플리케이션에서 code를 제공합니다. 여기서  한 번 더  code와, client id, client secret 3개의 정보를 담아 resource sever에 다시 보냅니다. resource sever는 자신이 보낸 code와 client id, client secret를 검증해서 access token를 보내줍니다. (*access token은 비밀번호처럼 사용되는 매우 중요한 정보입니다.)

애플리케이션은 access token을 이용해서 resource server에 접근하여 resoure를 획득하고  가공 후에  사용자에게 서비스를 제공합니다.

 

참고 강의

구글 API를 통해서 배우는 인증 (oauth 2.0)-생활코딩

https://opentutorials.org/course/2473/16571

 

구글 API를 통해서 배우는 인증 (oauth 2.0) - 생활코딩

수업소개 API를 사용하는데 큰 걸림돌은 인증입니다. 사용자에게 최적화된 서비스를 제공하기 위해서는 그 사용자의 정보에 접근할 수 있어야 합니다. 많은 서비스가 인증을 위한 방법으로 oauth

opentutorials.org

 

스프링애플리케이션에서 스프링 시큐리티를 사용할 때

 

“The type WebSecurityConfigurerAdapter is deprecated”

 

경고를 제거하는 방법✍🏻

 

 

 

많은 사람들은  아래 방법과 같은 WebSecurityConfigurerAdapter 추상 클래스를 확장하는 Spring 구성 클래스를 사용하는 데 익숙할 것입니다.

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
 
    @Override
    protected void configure(HttpSecurity http) throws Exception {
         
        // configure HTTP security...
         
    }
 
    @Override
    public void configure(WebSecurity web) throws Exception {
         
        // configure Web security...
         
    }      
}

해당 방법은

'Spring Security 버전 5.6.5' 또는 이전 버전'  

'Spring Boot 버전 2.6.8 또는 이전 버전'

에서는 문제가 없습니다.

 

그러나 프로젝트에서 Spring Security 5.7.1 이상 또는 Spring Boot 2.7.0 이상을 사용하는 경우 IDE

“The type WebSecurityConfigurerAdapter is deprecated” 경고가 표시됩니다.

 

그렇다면 Spring SecurityWebSecurityConfigurerAdapter 의 사용을 더 이상 사용하지 않는 이유 대안에 대해서 알아보겠습니다.

 

 


 

 

-이유🤷🏻‍♀️ 

스프링 프레임워크 개발자들은 사용자가 컴포넌트 기반 보안 구성으로 이동하기를 권하기 때문입니니다.

 


-대안🙆🏻‍♂️

WebSecurityConfigurerAdapterextends 하고 HttpSecurity WebSecurity 오버라이딩 하는 대신

아래 방법과 같이 SecurityFilterChain WebSecurityCustomizer 유형의 두 빈을 선언 합니다.

@Configuration
public class SecurityConfiguration {
         
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
     
    }
     
    @Bean
    public WebSecurityCustomizer webSecurityCustomizer() {
         
    }
         
}

 


-예시💻

security configuration 의 컴포넌트 기반 접근방식으로 바꾸는 코드 예제입니다.

 

 

 

먼저 아래와 같이 WebSecurityConfigurerAdapter 를 사용하는 일반적인 security configuration class 를 살펴보겠습니다 .

 

기존)

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
 
    @Bean
    public UserDetailsService userDetailsService() {
        return new ShopmeUserDetailsService();
    }
 
    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
     
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().antMatchers("/login").permitAll()
                .antMatchers("/users/**", "/settings/**").hasAuthority("Admin")
                .hasAnyAuthority("Admin", "Editor", "Salesperson")
                .hasAnyAuthority("Admin", "Editor", "Salesperson", "Shipper")
                .anyRequest().authenticated()
                .and().formLogin()
                .loginPage("/login")
                    .usernameParameter("email")
                    .permitAll()
                .and()
                .rememberMe().key("AbcdEfghIjklmNopQrsTuvXyz_0123456789")
                .and()
                .logout().permitAll();
 
        http.headers().frameOptions().sameOrigin();
    }
     
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/images/**", "/js/**", "/webjars/**"); 
    }
}

 

 

 

 

그리고 아래 대안법은 WebSecurityConfigurerAdapter를 사용하지 않은 예시입니다.

 

대안법)

package net.codejava;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
 
@Configuration
@EnableWebSecurity
public class SecurityConfiguration {
 
    @Bean
    public UserDetailsService userDetailsService() {
        return new ShopmeUserDetailsService();
    }
 
    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
 
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
     
        http.authorizeRequests().antMatchers("/login").permitAll()
                .antMatchers("/users/**", "/settings/**").hasAuthority("Admin")
                .hasAnyAuthority("Admin", "Editor", "Salesperson")
                .hasAnyAuthority("Admin", "Editor", "Salesperson", "Shipper")
                .anyRequest().authenticated()
                .and().formLogin()
                .loginPage("/login")
                    .usernameParameter("email")
                    .permitAll()
                .and()
                .rememberMe().key("AbcdEfghIjklmNopQrsTuvXyz_0123456789")
                .and()
                .logout().permitAll();
 
        http.headers().frameOptions().sameOrigin();
 
        return http.build();
    }
 
    @Bean
    public WebSecurityCustomizer webSecurityCustomizer() {
        return (web) -> web.ignoring().antMatchers("/images/**", "/js/**", "/webjars/**");
    }
 
}

 

해당 방법은 스프링 기반 애플리케이션의 스프링 시큐리티 사용에 있어서

“The type WebSecurityConfigurerAdapter is deprecated”경고를 지우는 방법입니다.

 

, WebSecurityConfigurerAdapter의 메스드 오버라이딩 정의 방법 대신 SecurityFilterChainWebSecurityCustomizer 빈들을 선언해야 합니다.

 

만약 코드를 변경하지 않고 유지 보수하기 위해서는 Spring Boot 버전을 2.7.0 미만으로 유지하거나 Spring Security 버전을 5.7.1 이전으로 유지해야 합니다.

 

참고 
https://www.codejava.net/frameworks/spring-boot/fix-websecurityconfigureradapter-deprecated

Spring Security - How to Fix WebSecurityConfigurerAdapter Deprecated

Written by  Nam Ha Minh

 

Spring Security - How to Fix WebSecurityConfigurerAdapter Deprecated

DetailsWritten by  Nam Ha Minh Last Updated on 01 June 2022   |   Print  Email In this short article, I’d like to share how to get rid of the warning saying that “The type WebSecurityConfigurerAdapter is deprecated” in Spring-based application w

www.codejava.net

 

'개발 > Spring Security' 카테고리의 다른 글

OAuth2.0- OAuth2.0의 이해  (0) 2022.06.29

ModelMapper란?

"서로 다른 클래스의 값을 한번에 복사하게 도와주는 라이브러리"
어떤 Object(Source Object)에 있는 필드 값들을 자동으로 원하는 Object(Destination Object)에 Mapping시켜주는 라이브러리이다.

 

http://modelmapper.org/

 

ModelMapper - Simple, Intelligent, Object Mapping.

Why ModelMapper? The goal of ModelMapper is to make object mapping easy, by automatically determining how one object model maps to another, based on conventions, in the same way that a human would - while providing a simple, refactoring-safe API for handli

modelmapper.org

 

 

 

 

 

 

 

 

org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint [null]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement

	
Caused by: org.hibernate.exception.ConstraintViolationException: could not execute statement
	
Caused by: org.h2.jdbc.JdbcSQLIntegrityConstraintViolationException: NULL not allowed for column "ITEM_DETAIL"; SQL statement:
insert into item (item_detail, item_nm, item_sell_status, price, reg_time, stock_number, update_time, item_id) values (?, ?, ?, ?, ?, ?, ?, ?) [23502-199]

개인 토이 프로젝트를 진행하며 상품 등록 테스트를 하던 도중 해당 오류를 발견하였습니다. 

 

ItemFormDto를 Item으로 변환하는 과정 중 제대로 변환이 되지 않고 Item 엔티티 컬럼에 null 값이 반영되어 있는 오류였습니다.

Item 엔티티에는 setter를 사용하지 않으려 setter를 제한하였으며, ItemFormDto에서 Item으로 변환하는 과정에서는 ModelMapper를 사용하였습니다.

 

 

 

@Entity
@Getter
@ToString
@Builder
@AllArgsConstructor
public class Item {

    @Id
    @Column(name = "item_id")
    @GeneratedValue
    private Long id;

    @Column(nullable = false, length = 50)
    private String itemNm;

    @Column(nullable = false)
    private int price;

    @Column(nullable = false)
    private int stockNumber;

    @Lob
    @Column(nullable = false)
    private String itemDetail;

    @Enumerated(EnumType.STRING)
    private ItemSellStatus itemSellStatus;

    private LocalDateTime regTime;

    private LocalDateTime updateTime;

    public Item() {
    }
}

-변환 될 Entity

 

 

 

@Getter
@Setter
public class ItemFormDto {

    private Long id;

    @NotBlank(message = "상품명은 필수 입력 값입니다.")
    private String itemNm;

    @NotNull(message = "가격은 필수 입력 값입니다.")
    private int price;

    @NotNull(message = "재고는 필수 입력 값입니다.")
    private int stockNumber;

    @NotBlank(message = "이름은 필수 입력 값입니다.")
    private String itemDetail;

    private ItemSellStatus itemSellStatus;

    private List<ItemImgDto> itemImgDtoList = new ArrayList<>();

    private List<Long> itemImgIds = new ArrayList<>();

    private static ModelMapper modelMapper = new ModelMapper();

    public Item createItem() {
        return modelMapper.map(this, Item.class);
    }

    public static ItemFormDto of(Item item) {
        return modelMapper.map(item, ItemFormDto.class);
    }


}

-변환할 Entity

 

 

 

 

 

Access level의 default 값이 public이다 따라서 해당 값을 private로 바꿔줄 필요가 있었던 것이었습니다.

 

 

 

 

public Item createItem() {
        return modelMapper.map(this, Item.class);
    }

따라서 해당 코드를

public Item createItem() {
        modelMapper.getConfiguration()
                .setFieldAccessLevel(Configuration.AccessLevel.PRIVATE)
                .setFieldMatchingEnabled(true);
        return modelMapper.map(this, Item.class);
    }

으로 변경하여 오류를 해결해 주었습니다.

 

 

 

 

 

참조 블로그

+ Recent posts