반응형

@Controller

- 일반적인 Spring MVC 컨트롤러

- 주로 view 를 반환하기 위하여 사용됨

 

View 반환

Return Type 은 String 으로 설정한다

return 에 들어오는 경로의 화면을 View Resolver 를 통해 반환한다

@Controller
@RequestMapping("/main")
public class MainController {

    @GetMapping(value = "/index")
    public String index(){
        return "/user/index";
    }
}

 

요청 처리 과정

1. Client 에서 URI 형식으로 요청

2. DispatcherServlet 에서 요청을 처리할 대상 조회

3. HandlerAdapter 를 통해 요청을 Controller 로 위임

4. Controller 에서 서비스로직 실행 후 view 반환

5. DispatcherServletViewResolver 를 통해 View 를 찾아 반환

 

View + Data 반환

Return Type 은 String 으로 설정한다

model 에 들어있는 data를 view와 함께 반환한다

@Controller
@RequestMapping("/main")
public class MainController {

    @GetMapping(value = "/index-model")
    public String indexModel(Model model){
        model.addAttribute("user", "");
        return "/user/index";
    }

    @GetMapping(value = "/index-param")
    public String indexParam(Model model, @RequestParam("userName") String userName){
        model.addAttribute("user", userName);
        return "/user/index";
    }

}

 

요청 처리 과정

1. Client 에서 URI 형식으로 요청

2. DispatcherServlet 에서 요청을 처리할 대상 조회

3. HandlerAdapter 를 통해 요청을 Controller 로 위임

4. Controller 에서 서비스로직 실행 후 viewData 를 함께 반환

 

Data 반환

페이지 이동없이 데이터만 Json 객체로 반환한다

@Controller
@RequestMapping("/main")
public class MainController {

    @GetMapping(value = "/index-rest")
    public @ResponseBody ResponseEntity indexRest() {
        //json type으로 반환하기 위하여 @ResponseBody 어노테이션이 필요함
        return ApiResponseEntity
                .builder()
                .ok();
    }
}

 

하지만 이런 경우 @Controller 를 쓰기보단 아래의 @RestController 어노테이션을 사용한다

 

@RestController

- RestFul 웹서비스의 컨트롤러

- @Controller + @RequestBody 의 기능을 한다

- ResponseEntity 형식으로 return

   ㄴ HttpStatus, data 를 Json 객체로 반환

 

 

Data 반환

페이지이동 없이 Data 만 Json 객체로 반환한다

아래 코드의 경

@RestController
@RequestMapping("/api/main")
public class MainRestController {

    @ResponseBody
    @GetMapping("")
    public ResponseEntity index() {
        return ResponseEntity
                .ok();
    }
}

 

요청 처리 과정

1. Client 에서 URI 형식으로 요청

2. DispatcherServlet 이 요청을 처리

3. HandlerAdapter 를 통해 요청을 Controller로 위임

4. Controller 에서 서비스로직 실행 후 객체 반환

5. 반환 Json 으로 Serialize 되어 Client 반환

 

 

viewResolver 필요한 프로젝트는 @Controller

api기능만 사용할 프로젝트는 @RestController 사용하면 된다!

728x90
반응형

 

1. 서버 메모리 swap

AWS 프리티어 무과금러는 .. Jenkins 배포에 앞서 꼭!!! 해야하는 작업이 있다

 

바로 메모리 swap

 

프리티어로 받은 서버로는.. 배포하다가 인스턴스가 죽어버린다

그러면 ip도 다시 발급받게되고ㅠㅠ 설정을 다 바꿔야하는게 너무귀찮으므로 꼭 메모리 swap 을 해야한다고 강조한다

 

자세한 과정은 아래 블로그에 너무 잘 정리해두셔서 꼭!! 방문을 해서 메모리 swap 처리를 하기 바란다

https://kth990303.tistory.com/361

 

[AWS] Swap File을 이용해 EC2 메모리 부족 현상을 해결해보자

작업을 하던 중 aws ec2 메모리 부족현상이 발생했다. 우리가 사용하고 있는 t4g.micro 인스턴스 유형은 램이 1GB였고, 이는 메모리 부족현상을 충분히(?) 볼 수 있을 정도로 작은 양의 메모리였다. 이

kth990303.tistory.com

 

2. AWS 서버에 Jenkins 권한 부여

Jenkins가 실제로 배포하려면 서버에 대한 읽기/쓰기/수정/삭제 권한이 있어야 한다

 

AWS 서버에 접속하여 root 계정으로 전환한다

$ sudo su

 

sudoers.d 폴더로 이동해서 jenkins 파일을 생성한다

$ cd /etc/sudoers.d
$ touch jenkins

 

 

jenkins에게 sudo 명령 실행권한을 부여하고 실행할때 password 를 물어보지않는 설정을 추가하고 해당파일 보안설정을 해준다

$ jenkins ALL=(ALL) NOPASSWD: ALL
$ chmod 0440 jenkins

 

근데 나는 이 방법으로는 jenkins 배포할때 권한이 자꾸 튕겨져나가서, 아래 커맨드도 실행했다

$ echo "jenkins ALL = (root) NOPASSWD:ALL" | sudo tee /etc/sudoers.d jenkins
$ sudo chmod 0440 /etc/sudoers.d/jenkins

2. Jenkins - Github 계정 연결

이제 Jenkisn 웹페이지에 접속해보자

 

Jenkins 에서 github 를 사용하려면 우선 github계정을 jenkins에 등록시켜야 한다

이때 사용할 github계정은 엄밀히 말하면 내가 배포하려고 하는 repository에 접근 가능한 github 계정이기만 하면 된다

 

대시보드 > Jenkins 관리 > System 으로 이동해보자

Github 이라는 영역이 보일텐데 여기서 git hub 계정추가가 가능하다

 

gitHub server 콤보박스를 선택하면 아래와 같은 영역이 노출될것이다

고대~~~로 냅두고 Credentials 하단의 Add 를 눌러준다

 

Credentials 는 Jenkins 에 등록된 github 계정목록이 노출되는 부분이지만

지금은 등록된 github 계정이 없으므로 none으로 보이는것일뿐이다

 

각 항목의 의미는 아래와 같다

username : gihub 계정 username
password : github 계정 password
ID : 해당 정보를 대표할 name이며, 해당 계정을 구분하기위한 용도로만 사용되는 대표이름. title
Description : 안적어도 됨

username은 github의 username 이고 ID는 jenkins 에서 사용할 id이다 즉, Credentials 목록에 노출될 text이다

 

이렇게 입력하면 비로소 Credentials 에 내 계정이 보인다

 

3. Jenkins - SSH 설정

 

앞으로 Jenkins 에서 AWS 서버에 접속하여 배포를 진행하겠지만, 우리는 해당서버에 pem/ppk 파일로 접속하고있기때문에 Jenkins에도 해당정보를 전달해주어야한다

 

1) SSH 플러그인 설치

기본적으로 Jenkins 에는 SSH 플러그인이 없기때문에 따로 설치해줘야 한다

 

아래 경로로 이동한다

Jenkins 관리 > 플러그인관리 > 설치가능 > Publish Over SSH 설치

 

설치완료되고 재시작 여부를 물으면 재시작해준다

 

재시작이 완료되면 System 관리 > Publish Over SSH 영역이 생겨있는것을 볼 수 있다

 

key 영역을 입력해줘야하는데, 다운받아놓은 pem 파일을 크롬에 던지면 텍스트가 주루룩 나온다

그것을 입력해주자

 

하지만 이것은 pem 파일만 등록된거고, 그래서 어느 서버에 적용할건지 설정해줘야한다.

하단의 추가 버튼을 클릭하여 SSH Server 를 등록하자

 

 

name : 해당 서버를 지칭할 이름. 중요하지않다 title 개념
hostname : AWS IP주소
username : 유저명 (우리의 경우는 ubuntu라고 적는다)
remote directory : 젠킨스로 생성된 배포파일의 root 경로

 

 

여기서 어려운 부분은 remote directory 인데, 우선 AWS 서버 내 배포파일이 저장될 위치를 알아서 정해보자.

 

나는 home 디렉토리에 ubuntu 폴더를 만들었다.

그래서 위와같이 Remote Directory 는 /home/ubuntu 가 되었다

 

이렇게 하면 Jenkins가 실제로 배포할때 서버에 접속할 수 있게 되었다

 

3. 배포 item 생성 - Free Style

자 드디어 배포를 해보자!!

 

Dashboard 에서 새로운 Item 을 추가해준다

 

 

다양한 선택지가 있지만, 우선 Freestyle project 를 만들어보겠다

해당 프로젝트명 혹은 구분가능한 이름을 input에 입력해주자

 

두가지 옵션의 간단한 차이는 아래와 같다

Freestyle project ?
-> jenkins 설정으로만 배포한다
Pipeline ? 
-> 프로젝트 소스코드에 내가 직접 커맨드를 입력한 정보를 기반으로 배포한다

 

 

 

item 을 생성하면 또!! 설정창이 나온다 ㅎㅎ

기존에 설정했던것이 서버에 대한 설정이라면, 지금은 repository 에 대한 설정이라고 보면된다

 

1) Github 설정

설명 부분에 해당 프로젝트의 설명을 간단히 입력해주고 하단의 Github project 체크박스를 선택한다

그리고 나의 repository 주소를 넣어준다. git clone 할때 입력되는 http 주소이다

 

밑에 내리면 소스코드관리 -> Git 을 선택한다

Repository URL 은 위에서 입력한 github 주소와 똑같이 입력해주고, Credentials도 기존에 설정된 계정을 선택한다

 

하지만!!!

 

만약 private github repository 를 사용한다면 해당 계정으로는 오류가 발생한다

 

원인은, 정책변경으로 인하여 user/password 방식으로 등록한 github계정으로는 해당 repository에 접근이 불가하다

토큰방식으로 등록된 github 계정이 필요하다

 

또한!! 해당 private repository를 생성한 계정이 필요하다.

 

repository 가 public 이라면 아래 과정은 넘어가주길 바란다.

만약 private 라면 Credentials 하단의 Add를 눌러서 토큰방식의 계정을 추가해보자

 

secret 에 github에서 발급받은 토큰을 입력해야한다

Kind → Secret Text 선택 (토큰 인증방식)
Domain : Global credentials
Secret : 깃헙에서 발급 받은 토큰 입력
ID : 해당 정보를 대표할 name이며, 해당 계정을 구분하기위한 용도로만 사용되는 대표이름. title
Description : 안적어도 됨

 

 

2) Build Step 설정

 

github 설정하고 내리다보면 빌드유발..빌드환경..등등 많이 있지만 

안타깝게도 AWS 프리티어는 빌드를 할때마다 data transfer fee가 청구된다 ㅠㅠ 그래서 한달에 정해진 용량이있는데..

내가봣을땐 한달에 30번정도 하면 용량에 딱 맞았던거같다. 그래서 github hook을 연결해서 빌드하는 작업은 건너뛰었다

 

이제 각 프로젝트에 맞는 빌드과정을 진행하면 되는데, 나는 Spring boot - java 프로젝트를 배포하였다

 

 

gradle clean build를 해준다는 의미이다

echo ">> grant permission"
chmod +x gradlew

echo ">> project clean Build Start"
./gradlew clean build

 

3) 빌드 후 조치

빌드가 되면 어떤 작업이 필요한지 설정한다

spring 프로젝트의 경우 실행중인 포트가 있다면 종료하고 jar 파일을 실행시키도록 하였다

나는 이미 선택해서 회색으로 되어있지만 Send build artifacts over SSH 선택한다

 

이제 또 버라이티하게 입력할 영역이 노출되지만 침착하게 입력해보자

나는 spring boot 멀티모듈 프로젝트였기에 경로가 좀 보잡하다

 

각 항목에 대한 부가설명은 아래와 같다

 

name :: 프로젝트 명

Transfer :: 어느경로의 배포파일을 어디로 복사할건지 정하는 영역

 

Source files : build/libs/*.jar 로 입력

 - 만약 멀티모듈이라면 해당 모듈명/build/libs/*.jar 로 입력

 

Remove prefix : 배포 시 Source files 의 경로에서 제거되어야하는 경로를 입력

 - 배포파일 즉 jar 파일은 System 설정에 등록된 Remote Directory 경로 + Source files 경로로 다운로드될건데

    공란으로 둔다면 /home/ubuntu/build/libs/*.jar 경로로 다운로드될것이다. 그것이 싫다면 build/libs 를 입력하자.

    만약 멀티모듈이라면 해당모듈명/build/libs/*.jar 로 입력

 

Remote directory : 배포파일 위치할 폴더명

 - Remove prefix 까지 설정하면 /home/ubuntu/*.jar 경로로 다운로드 될텐데, 이러면 어떤프로젝트인지 구분이 되지 않으니

    구분하고싶은 폴더명을 입력하면 된다. 즉 /home/ubuntu/폴더명/*.jar 의 경로로 받아지게 하기 위한 폴더명이다

 

Source files : build/libs/hello.jar
Remove prefix : build/libs
Remote directory : helloJava

이렇게 설정된 경우, 최종적으로 배포파일은 /home/ubuntu/helloJava/hello.jar 경로로 배포될것이다

 

Exec command : 옮기고나서 실행할 커맨드

 

나는 8080포트번호를 죽이고 jar 를 실행하는 커맨드를 입력했다

pid="$(lsof -t -i :8080 -s TCP:LISTEN)";

if [ "$pid" != "" ]; then
  kill -9 $pid;
  echo "$pid process kill complete"
else
  echo "pid is empty"
fi

cd /home/ubuntu/폴더명

java -jar 배포파일.jar &

 

만약 멀티모듈을 사용한다면 Add server 로 다른 모듈도 추가해주면 된다

 

진짜끝!!

 

3. 배포 item 생성 - Pipeline

백단은 freestyle 로 배포구성을 마쳤다

이제 프론트를 해야하는데, 나의 경우는 react-next.js 로 구성된 프로젝트였다

때문에 해당 프로젝트를 jenkins에서 배포하기 위하여 node js 플러그인 설치가 필요하였다

 

1) node js 플러그인 설치

Dashboard > Jenkins 관리 > Plugins > Avaliable plugins > node js 검색하여 설치하자

 

2) Node Js 이름설정

설치가 완료되었으면 아래 경로로 이동하여 다운받은 nodejs 의 이름을 설정해준다

나는 nodejs 라고 설정하였다

 

Dashboard > Jenkins 관리 > Tools → NodeJs → name 설정

 

3) Jenkinsfile 추가

 

Pipeline으로 Jenkins 가 배포하기위해서, 프론트 프로젝트 소스코드를 수정해야한다

 

프로젝트 root에 Jenkinsfile 파일을 추가하고 아래의 코드를 입력한다

확장자 없고 파일명은 "Jenkinsfile" 딸랑 하나만 놓으면 된다

pipeline {
     agent any
     tools {nodejs "nodejs"}

     stages {
        stage("Build") {
            steps {
                sh "npm install"
                sh "npm run build"
            }
        }
        stage("Deploy") {
            steps {
                script {
                    sh 'chmod +x ./script/deploy.sh'
                    sh './script/deploy.sh'
                }
            }
        }
    }
}

 

상단의 코드에서 tools {nodejs "nodejs"} 라고 되어잇는 부분이 있는데,

"" 쌍따옴표 안에 들어가는 이름은 위에 Jenkins 에서 설정한 Node Js 의 이름을 정확히 넣어야한다

 

그리고 프로젝트 root에 script 폴더를 만들고 deploy.sh 파일을 추가하자

왜냐하면 상단의 코드에서 Deploy 부분에 script/deploy.sh 의 작동을 하기 때문이다

pid=$(sudo lsof -ti:3000)


echo "========= front deploy start ========="

if [ -n "$pid" ]; then
  sudo kill -9 $pid
  echo "kill ${pid} process..."
  nohup npm start &
  exit
else
  nohup npm start &
  exit
fi

echo "nginx restart..."

systemctl restart nginx

echo "========= front deploy end ========="

 

3000번 포트를 죽이고 nginx restart 해주는 설정이다

 

4) item 추가

 

자 이제 프론트 배포를 해보자ㅠㅠ

 

Pipeline 으로 아이템을추가하고 설명, github repository 주소를 입력한다

 

하단에 내리다보면 Pipeline 설정을 할수있다

위에 입력한 repository 와 동일한 url 을 입력해주고 만약 private repository 라면 위에서 설정한 토큰방식의 github 계정과 연결해준다

 

그리고 배포되어야하는 브랜치명을 입력해준다

 

 

이렇게.. 프론트/백 배포 설명을 마쳐본다

 

Freestyle 은 Jenkins 의 설정에 따라 배포되고 

pipelene은 프로젝트 내 세팅해놓은 설정에 따라 배포된다

 

이 과정을 따르면 AWS 프리티어 인스턴스에 spring boot 로 개발된 백단 프로젝트를 AWS 에 배포하고

react + next.js 로 개발된 프론트 프로젝트를 AWS 에 배포할수있다!

 

728x90
반응형

 

내가 aws 서버에 배포할 프로젝트는 총 2개이다

프론트 react+next.js
백엔드 springboot java

 

jenkins 설치에 앞서, 실제로 jenkins가 설치되고 실행시키기 위한 react 와 jdk를 설치해야한다

 

1. react 설치

아래 커맨드로 react 를 설치한다

설치하기에 앞서 업데이트 및 업그레이드를 해주는게 좋다

apt update ?
패키지 목록을 최신 상태로 업데이트
apt upgrade ?
시스템에 설치된 모든 패키지를 최신 버전으로 업그레이드
$ sudo apt update
$ sudo apt upgrade

 

현재는 npm이 없는 상태라서, npm을 install 받아준다

npm ?
Node.js환경에서 사용되는 패키지를 관리하고 설치하는 도구
간단히 말하면, 프론트에서 사용되는 다양한 소프트웨어를 다운받으려면 npm이 꼭 있어야 한다
$ sudo apt install npm

 

무사히 npm install 이 완료되었다면 react 를 설치한다

$ npm install -g create-react-app

 

만약 권한문제로 설치가 안된다면 아래 커맨드 입력 후 진행한다

$ sudo su
$ npm install -g create-react-app

 

install이 완료되었다면 아래 커맨드로 잘 설치되었는지 확인한다

$ create-react-app --version

 

버전명이 나온다면 설치완료된것!

 

2. JDK 설치

내가 설치하고 싶은 jdk 버전으로 설치한다

나는 jdk 17을 설치하였다

$ sudo apt-get update
$ sudo apt-get upgrade
$ sudo apt-get install openjdk-17-jdk

 

3. AWS 인바운드 규칙 수정

본격적인 jenkins 설치에 앞서 AWS 인바운드 규칙수정을 해줘야한다

 

추후 해당 jenkins 에 접근하려면 http://ip주소~~/포트번호 이렇게 접근을 해야하기때문에, jenkins 용도로 사용할 포트번호를 정하고 AWS에서 접근권한을 줘야하기 때문이다

 

AWS 웹페이지 -> 콘솔 -> EC2 -> 보안그룹 -> 인스턴스명 선택 -> 인바운드 규칙 편집

나는 8081 이라는 포트를 사용하였다.

 

본인이 원하는 포트번호를 정하고 나중에 포트번호를 까먹을 수 있기 때문에 메모란에 jenkins 라고 적어주자.

 

4. Jenkins 설치

AWS에서 제공해주는 Jenkins 설치기능을 사용하여도 되지만... 유료다!

나는 철저한 무과금을 위하여 직접 jenkins를 설치하였다


설치하는 과정에서 jenkins 의 ssh key 변경 등의 이슈가 있었고, 아래 과정이 나중에는 안먹힐수도있지만 현재까지는 잘 작동되는것을 확인하였다

 

1) 서버접속

윈도우/맥 OS 에서 기존에 만들어둔 서버에 접속한다

2) Jenkins 설치

아래 커맨드를 순서대로 실행한다

$ wget -q -O - https://pkg.jenkins.io/debian/jenkins-ci.org.key | sudo apt-key add -
$ echo deb http://pkg.jenkins.io/debian-stable binary/ | sudo tee /etc/apt/sources.list.d/jenkins.list

 

그리고 아래 커맨드로 젠킨스를 실제로 다운로드 받는데

$ sudo apt install jenkins

 

오류가 발생했다

ubuntu@ip-172......:~$ sudo apt-get install jenkins
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
Package jenkins is not available, but is referred to by another package.
This may mean that the package is missing, has been obsoleted, or
is only available from another source

E: Package 'jenkins' has no installation candidate

 

이런 오류가 발생되었다면,

https://pkg.jenkins.io/debian-stable/ jenkins 가이드를 참고하여 커맨드를 새로 입력한다

보통 커맨드랑 다르게 엔터가 들어가있을건데 그냥 그대로 복사해서 입력하면된다

 

$ curl -fsSL https://pkg.jenkins.io/debian-stable/jenkins.io-2023.key | sudo tee \
    /usr/share/keyrings/jenkins-keyring.asc > /dev/null
$ echo deb [signed-by=/usr/share/keyrings/jenkins-keyring.asc] \
    https://pkg.jenkins.io/debian-stable binary/ | sudo tee \
    /etc/apt/sources.list.d/jenkins.list > /dev/null

 

무슨 key가 변경된거같은데 이렇게 입력해도 아무 반응이 없을것이다

긴가민가하지만, 다시 install을 시작해보자

$ sudo apt update
$ sudo apt install jenkins

 

잘 될것임!! 안되면 구글링 각 ㅠㅠ

설치 중 아래와 같은 문구가 나온다면 N 을 해주자

 

Configuration file '/etc/gnome/defaults.list'
 ==> Modified (by you or by a script) since installation.
 ==> Package distributor has shipped an updated version.
   What would you like to do about it ?  Your options are:
    Y or I  : install the package maintainer's version
    N or O  : keep your currently-installed version
      D     : show the differences between the versions
      Z     : start a shell to examine the situation
 The default action is to keep your current version.
*** defaults.list (Y/I/N/O/D/Z) [default=N] ?

 

설치가 잘 됐는지 확인하기위해 아래 커맨드로 jenkins 상태를 확인한다

$ sudo systemctl start jenkins
$ sudo systemctl status jenkins

 

하지만 상태를 보면 마지막에 httpPort = 8080으로 되어있을것이다


이것을 AWS에서 보안규칙변경한 8081로 바꿔보자

3) jenkins 포트변경

jenkins 포트변경 하려면 jenkins.service 파일을 수정해야하는데 해당 파일은 readOnly라서 수정 권한이 필요하다

권한 부여 후 수정해보자

 

chmod 777 ?
모든 사용자에게 읽기, 쓰기, 실행 권한을 부여
$ sudo chmod 777 /usr/lib/systemd/system/jenkins.service
$ sudo vim /usr/lib/systemd/system/jenkins.service

 

Environment jenkins port 설정을 추가하고 저장해준다

해당파일을 다시 readOnly로 만들어주기 위해서 권한을 변경한다

 

chmod 444 ?
모든 사용자에게 읽기만 가능한 권한을 부여
$ sudo chmod 444 /usr/lib/systemd/system/jenkins.service

 

해당 변경사항을 정상적으로 사용하기위해서 jenkins 를 재시작 해야한다

 

deamon-reload ?
새로운 또는 변경된 서비스 파일을 다시 읽도록 명령
$ sudo systemctl daemon-reload
$ sudo service jenkins restart
$ sudo systemctl status jenkins

 

jenkins의 status 를 살펴보면 정상적으로 activate 되었으며 httpPort = 8081 로 확인된다

하단에 Password 관련 안내문구가 있는데 암호화된것같은 저 문장이 jenkins 초기 password 이다

해당 password text를 복사해둔다

 

5. Jenkins 접속

이제 브라우저에서 Jenkins 에 접속해보자

http://ip주소:젠킨스포트번호

 

처음 접속하면 아래와 같은 화면이 보일것이다

서버에서 제공받았던 password를 입력해서 접속하자

 

혹시 password를 모른다면 아래 커맨드로 password를 확인할 수 있다

$ sudo cat /var/lib/jenkins/secrets/initialAdminPassword

 

1) 초기설정

Install suggested plugins 선택한다

select 는 커스텀같아서.. 무리!

2) 계정생성

앞으로 jenkins 로그인 시 사용할 계정을 생성한다

 

3) IP 설정

AWS IP 주소 + 포트번호를 적어주면 된다

 

혹시!! aws 인스턴스 ip가 탄력적 ip 설정이 되어있지 않으면 인스턴스 올렸다 내릴때마다 ip주소가 바뀌는데

 

추후 Jenkins에 설정된 ip주소를 바꿔야한다면 브라우저에서 아래와 같은 경로로 진입해서 바꾸면된다

Jenkins 접속 > Dashboard > Jenkins 관리 > System > Jenkins URL 변경

 

이렇게 Jenkins 설치 및 계정생성이 완료되었다

 

다음 과정으로는 github 계정연결, ssh 설정, 빌드후조치, item 생성, 메모리 swap을 해야하는데

포스팅이 너무 길어져서.. 다음 포스팅에서 다뤄야겠다 :)

 

 

728x90
반응형

1. Nginx 설치

서버에서 아래와 같이 nginx를 설치한다

$ sudo apt-get update
$ sudo apt-get upgrade
$ sudo apt-get install nginx

 

apt 업데이트를 해주고 nginx 설치가 되었으면 아래 커맨드로 잘 구동되는지 확인한다

$ sudo service nginx status

 

그리고 서버도 들어가서 nginx 로고를 확인하면 끝!

 

나는 react + next.js 프로젝트를 jenkins로 배포할 예정이며
next.js와 jenkins pipeline 에서 프록시설정을 진행할것이기때문에 nginx에 별도의 proxy 설정은 하지않았다

 

하지만 만약 proxy 설정이 필요하다면 아래 번외를 참고!

 


 

아래는 참고용 Nginx 커맨드

$ sudo service nginx status # nginx 상태 확인
$ sudo service nginx start # nginx 실행
$ sudo service nginx restart # 중지 후 재실행
$ sudo servcie nginx reload # 수정된 파일 적용하여 연결을 끊지 않고 재실행
$ sudo service nginx stop # nginx 중지

 

기본적으로 Nginx는 서버가 부팅될 때 자동으로 시작된다

$ sudo service disable nginx # 자동 시작 비활성화
$ sudo service enable nginx # 자동 시작 활성화

 

 

번외

만약 nginx 에 proxy 설정을 해야한다면 아래 커맨드로 nginx 설정파일 진입

$ sudo vi /etc/nginx/sites-avaliable/default

 

위 이미지와 같이 location 과 root 를 설정해주고 저장

 

root : nginx 실행되는 프로젝트 위치
location : ip 혹은 도메인명/~ 쳤을때 연결되어야하는 proxy 포트 연결설정

728x90
반응형

1. Maria DB 설치

많고 많은 DB 중에서 Maria DB 를 선택한 이유는 실제 운영을 염두하여 비용이 들지않게 하기 위하여 선택했다.

 

 

1) APT 업데이트

Maria DB 설치에 앞서 시스템 업데이트를 해줄필요가 있다

 

apt ?
Advanced Package Tool의 약자, 리눅스 시스템에서 소프트웨어 패키지를 설치/업데이트/제거 하는 명령어와 관리도구

 

소프트웨어를 리눅스서버에 설치하기에 앞서 업데이트를 해주는게 좋다

 

$ sudo apt update

 

2) Maria DB install

이제 실제 Maria DB를 다운로드 한다

 

$ sudo apt-get install -y mariadb-server

$ sudo apt install -y mariadb-server

 

3) 비밀번호 설정

ROOT 계정의 비밀번호를 설정한다

$ sudo mysql_secure_installation

 

커맨드를 입력하면 아래와 같은 선택창이 나온다
차례대로 본인에게 맞는 설정을 선택한다

Switch to unix_socket authentication [Y/n]
-> 패스워드를 사용하기위해 N
Change the root password? [Y/n] Y
-> 패스워드 변경을 위해 Y
Remove anonymous users? [Y/n] Y
-> 보안을 위해 익명사용자 제거 Y
Disallow root login remotely? [Y/n] n
r-> oot 계정 원격접속 필요 시 N, 아니면 Y
Remove test database and access to it? [Y/n] Y
-> test DB를 유지하려면 N
Reload privilege tables now? [Y/n] Y
-> 지금까지 설정한 내용 적용하려면 Y

 

ROOT 계정설정이 완료되었다
실제로 외부에서 접근하려면 root 계정을 그대로 쓰는것보다 새로운 계정을 하나 만들고 그것을 사용하는게 좋다

 

2. DB 외부 접근 설정

개발툴 등 외부에서 접근하기위한 user를 생성한다

 

1) 외부접근 계정생성

아래 커맨드로 mysql에 접속한다

$ sudo mysql

 

접속되었으면 계정을 생성한다

mysql> create user '아이디'@'%' identified by '비밀번호';
mysql> grant all privileges on *.* to '아이디'@'%' with grant option;

 

위 커맨드를 한줄씩 입력하면되고, 아이디/비밀번호는 본인이 설정하면된다
그리고 다른 커맨드와 다르게 세미클론을 꼭 붙여준다

 

아래는 예시이다

 

아이디 : aaa
비밀번호 : aaa!!

 

mysql> create user 'aaa'@'%' identified by 'aaa!!!';
mysql> grant all privileges on *.* to 'aaa'@'%' with grant option;

 

2) 외부접근 가능포트 설정

외부접근용 계정을 생성하였으니, 외부에서 접근하기위하여 접근가능포트 설정을 해야한다

우선 아래 커맨드로 DB 접속을 해제한다

mysql> exit

 

해당 DB를 외부에서도 접근하기 위한 포트설정이 필요하다

$ sudo ufw allow out 3306/tcp
$ sudo ufw allow in 3306/tcp

 

위 2개의 커맨드를 입력했을때 Rules update 나오면 성공이다

 

3) 접근 가능 ip 수정

포트를 뚫었어도 접근 ip설정도 같이 수정해야한다.

아래의 커맨드로 mysqld 설정을 확인한다

$ mysqld --print-defaults

 

아래와 같은 화면이 보일것이다

--bind-address=127.0.0.1 라는 구문이 있는데 이것은 로컬서버에서만 접근한다는 설정이다


따라서 이것을 전체접근가능으로 변경해주어야한다

설정을 변경하기위하여 아래 커맨드로 config 파일을 편집한다

$ sudo vim /etc/mysql/mariadb.conf.d/50-server.cnf

 

해당 커맨드 입력하면  아래와 같이 확인될것이다

여기서 변경할것 2가지 이다

  • bind-address -> 0.0.0.0
  • mysqld 밑에 lower_case_table_names = 1 추가 (테이블 대소문자 구분안함 설정)

아래와 같이 변경해준다

 

해당 설정을 적용시키기 위하여 DB 재시작한다

$ sudo systemctl restart mysqld

 

그리고 설정이 잘 적용되었는지 확인한다

$ mysqld --print-defaults

 

4) Database 생성

만약 테스트 db 말고 별도 DB를 생성하고 싶다면 아래와 같은 과정을 따른다

$ sudo mysql
mysql> show databases;

 

DB에 접속하여 database 목록을 확인한다

기존 database 이름과 겹치지않도록 주의하여 새로운 database를 생성한다

mysql> create database 새로운db명;

 

아래 커맨드로 제대로 생성됐는지 다시 확인해본다

mysql> show databases;

 

아래와 같이 --bind-address 가 변경된것을 확인할 수 있다

 

3.  AWS 외부 접속 설정

위와 같은 과정으로 리눅스서버의 설정은 완료하였지만 실제로 외부에서 (개발툴 등)접근하려면 AWS의 인바운드 규칙도 같이 변경해야한다

 

1) AWS 보안 인바운드 규칙 편집

규픽 편집을 위해 아래 경로로 진입한다

AWS 웹페이지 -> 콘솔 -> EC2 -> 보안그룹 -> 인스턴스명 선택 -> 인바운드 규칙 편집

 

유형 : MYSQL
프로토콜 : TCP
포트범위 : 3306
소스 : 0.0.0.0 (모든 IP접근가능)
설명 : 해당 포트에 대한 설명

 

필요에 따라 소스는 본인의 IP를 적어도되고, 다양한 규칙이 생겼을때 구분하기위하여 설명을 추가 하였다

모든것이 완료되었으면, 본인의 개발툴에서 접속이 가능한지 Test Connection 해보자

 

잘되길 바라며.. !

728x90
반응형

 

시작하며

 

개인적으로 진행한 프로젝트이므로 틀린 정보가 있을 수 있습니다
언제든지 댓글 달아주세요 :)


1. AWS 프리티어 계정 생성

사이트 URL : https://aws.amazon.com

AWS 프리티어 계정 생성을 위하여 사이트에 방문하고 가입합니다

 

AWS 프리티어 계정이란?
아마존 웹 서비스에서 지원하는 무료로 서버를 사용할 수 있는 권한입니다

 

인스턴스란?
서버 라고 생각하면 됩니다

 

EC2 인스턴스란?
AWS 에서 사용하는 인스턴스를 EC2 인스턴스라고 합니다

 

2. EC2 인스턴스 생성

콘솔 홈 화면 하단의 위젯 ->가상머신 시작 클릭

1) 인스턴스 설정

 

 

이름 : 프로젝트명 입력
Quick Start : Ubuntu - Ubuntu Server 22.04 LTS(HVM) SSD Volume Type

프리티어 사용 가능이라고 적혀진 옵션을 선택

 

인스턴스 유형 : t2.micro
키페어 : 새 키페어 생성 (키페어 있을경우 콤보박스에서 기존 키페어 쓰거나 새로 생성)

키페어란 ?
putty, fileZilla 등으로 서버접속이 가능한 .ppk .pem 파일
키페어 이름은 단순 ppk/pem 파일명이므로 큰 의미를 갖지않으니 적당히 정하기

 

네트워크 설정 : 보안그룹생성 - ssh 트래픽허용 (기존 보안그룹이 있다면 콤보박스에서 선택)

 

스토리지 구성 : 20g (20g가 프리미어 최대 제공 스토리지)
IAM 생성 : 필요에 따라 생성

 

IAM 이란 ?
관리자 접근권한을 관리하는 서비스이며 다수의 관리자가 모든 데이터에 접근하면 위험하므로, 관리자 접근 및 행동을 제어하기위한 설정

 

탄력적 IP 생성 : 유료서비스. 한달에 1000원이내. 인스턴스에 특정 IP를 고정해서 사용할 수 있다.

유료서비스이므로 나는 하지않았지만, 발급방법은 아래와같다

 

2) 탄력적 IP 생성

  1. IP 주소 할당

 

   2. IP 주소 생성완료

   

   3. 인스턴스 - IP 연결

3. 서버 접속

만들어두었던 EC2 서버에 접속한다

 

만약 아래의 방법으로 접속이 안된다면, aws 설정으로 22번 포트가 안뚫려있을수도있다


AWS 웹페이지 -> 콘솔 -> EC2 -> 보안그룹 -> 인스턴스명 선택 -> 인바운드 규칙 편집
여기서 22번 포트를 추가해보자

1) window 접속방법

  • 접속 툴 : putty
  • 접속 키 : ppk
    AWS에서 발급받은 키페어파일이 pem 파일이라면, ppk 파일로 변환해야한다
    ppk 파일변환하려면 puttygen 실행 -> key 변환해준다
  1. ppk 파일연결
    putty -> Connection -> SSH -> Auth -> Credentials -> private key file for auentication 여기에 ppk 파일 첨부

      save 버튼은 따로없다 바로 session 탭으로 이동한다

 

    2. 서버접속
      aws콘솔 인스턴스에서 확인되는 퍼블릭 IPv4 주소 를 Host Name 에 넣고 open 하면 아래와 같이 접속된다
      login as : 라고 나올텐데 당황하지않고 ubuntu 라고 입력해주자

2. mac 접속방법

  • 접속 툴 : terminal
  • 접속 키 : pem
    AWS에서 발급받은 키페어파일이 ppk 파일이라면, pem 파일로 변환해야한다
  1. ppk to pem
    mac에서 변환하려면 아래 커맨드로 putty 를 설치하고
$ brew install putty


        아래 커맨드로 변환한다

$ puttygen [원본.ppk] -O private-openssh -o [사본.pem]

 

    2. 서버접속
        pem 파일이 있는 위치에서 terminal 실행한다
        AWS 웹페이지 -> 인스턴스명 우클릭 -> 연결 -> SSH클라이언트 탭 클릭하여 예) 부분에 나와있는 명령어를 그대로 입력한다

4. 서버 time zone 세팅

새로발급받은 서버는 보통 US 미국기준으로 타임존 세팅이 되어있다.
아래 커맨드로 타임존을 확인한다

$ sudo date

 

타임존이 우리나라가 아니며, 우리나라로 세팅을 해야한다면 아래와 같은 커맨드를 입력한다

 

$ sudo timedatectl set-timezone 'Asia/Seoul'

 

타임존을 다시 조회해본다면 서울로 바뀌어있을것이다

 

서버세팅 완료~

 

728x90
반응형

 

1. dependency thymeleaf 추가

implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'

 

2. dependency thymeleaf 레이아웃 가능하게하도록 추가

implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect'

 

이제 환경설정이 끝났으니, 레이아웃 제작

이런 루트로 생될 예정임.

 

3. footer.html 생성

<html lagn="ko" xmlns:th="http://www.thymeleaf.org">
<!--footerFragment 선언-->
<div th:fragment="footerFragment">

  <style>
    .admin-footer {
      background-color: aliceblue;
      padding-left: 10%;
      min-height: 150px;
    }
  </style>

  <div class="admin-footer">
    <h3>footer area</h3>
  </div>
</div>
</html>

th:fragment 설정으로 fooerFragment 매핑시켜줌

 

 

4.header.html 생성

<html lagn="ko" xmlns:th="http://www.thymeleaf.org">
  <!--headerFragment 선언-->
  <div th:fragment="headerFragment">

    <style>
      ul {
        list-style-type: none;
        margin: -19px -9px -9px -7px;
        width: 10%;
        padding:50px 0 0 0;
        background-color: #f1f1f1;
        position: fixed;
        height: 100%;
        overflow: auto;
      }
      li a {
        display: block;
        color: #000;
        padding: 8px 16px;
        text-decoration: none;
      }

      li a.active {
        background-color: #555;
        color: white;
      }

      li a:hover:not(.active) {
        background-color: #555;
        color: white;
      }

    </style>

    <ul>
      <li><a href="/admin">대시보드</a></li>
      <li><a href="/admin/statistics/list">방문 통계</a></li>
      <li><a href="/admin/user/list">회원 관리</a></li>
      <li><a href="/admin/menu/list">메뉴 관리</a></li>
      <li><a href="/admin/expo/list">일정 관리</a></li>
      <li><a href="/admin/tag/list">태그 관리</a></li>
      <li><a href="/admin/event/list">이벤트 관리</a></li>
    </ul>

  </div>
</html>

th:fragment 설정으로 headerFragment매핑시켜줌

 

5. default_layout.html 생성

<!DOCTYPE html>
<html lagn="ko"
      xmlns:th="http://www.thymeleaf.org"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<head>
  <meta charset="UTF-8" />
  <title>타이틀/title>
  <meta name="viewport" content="width=device-width, maximum-scale=1.0, minimum-scale=1, user-scalable=yes,initial-scale=1.0" />

  <!-- 공통 css -->
  <link rel="stylesheet" th:href="@{/content/css/common.css}" >
  <th:block layout:fragment="css"></th:block>

  <!-- 공통 script -->
  <th:block layout:fragment="script"></th:block>
</head>

<body>
<!-- header -->
<th:block th:replace="~{fragments/header :: headerFragment}"></th:block>

<!-- content -->
<th:block layout:fragment="content"></th:block>

<!-- footer -->
<th:block th:replace="~{fragments/footer :: footerFragment}"></th:block>

</body>
</html>

만약!!

~{flagments 이게 활성화되어있지않고 녹색으로 죽어있다면(인텔리제이 기준)

gradle 새로고침 해줘야함

 

6.index.html 생성

 

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" <!-- 레이아웃을 쓴다는 설정-->
      layout:decorate="~{layouts/default_layout}"> <!-- 레이아웃을 쓴다는 설정-->

<!-- index.html에서만 사용할 CSS -->
<th:block layout:fragment="css">
    <!--<link rel="stylesheet" th:href="@{/css/common.css}" >-->
</th:block>
<!-- index.html 에서만 사용할 스크립트 -->
<th:block layout:fragment="script">
    <!--    <script th:src="@{/js/page/home.js}"></script>-->
</th:block>

<!-- Content -->
<div layout:fragment="content">
    <div class="container">
        <h3>대시보드</h3>
        <img th:src="@{/content/image/dash1.PNG}"/>
        <img th:src="@{/content/image/dash2.PNG}"/>
    </div>
</div>
</html>

 

이제 빌드하면 레이아웃 정상적으로 보일것임!!!

 

그리고.. 만약 안된다면 build.gradle 새로고침~~

728x90
반응형

 

1. 페이지 이동

 

1) 이동할 페이지 생성

<template>
  <div>board List</div>
</template>

<script>
export default {
  name : 'boardList'
}
</script>

?궁금증 export default name 은 왜 하는걸까? 없어도 페이지이동 되던데..

 

나는 pages 에 새 폴더 만들어서 페이지를 만들어줬다.

 

2) 이동

<nuxt-link to="/board/boardList">ttt</nuxt-link>

<nuxt-link> 태그를 이용해서 페이지이동한다.

to 속성에 이동할 페이지의 디렉토리 정보를 넣어준다.

 

nuxt 특징은 pages에 페이지 만들어주면 .nuxt -> router.js 에 자동으로 라우터가 생성된다.

vue를 쓰면 항상 라우터설정을 해줘야되는데 자동으로 생성되기때문에 좋은듯!

 

2. 레이아웃

 

1) header.vue 생성

<template>
  <header>
    <div id="nav">
      <nuxt-link to="/">Home</nuxt-link> |
      <nuxt-link to="/about">About</nuxt-link> |
      <nuxt-link to="/board/list">게시판</nuxt-link>
    </div>
  </header>
</template>

2) footer.vue 생성

<template>
  <footer>
    <h3>footer 입니다.</h3>
  </footer>
</template>

3)index.vue에 추가

<template>
    <page-header/>
    <nuxt-link to="/board/boardList">게시판 이동</nuxt-link>
    <page-footer/>
</template>

-> 이렇게 하면 에러남!

<template>
  <div>
    <page-header/>
    <nuxt-link to="/board/boardList">게시판 이동</nuxt-link>
    <page-footer/>
  </div>
</template>

template 아래 div 혹은 selection 도 좋으니.. 최상위태그 하나 만들어서 씌워줘야한다.

 

 

 

 

728x90

'FrontEnd > Nuxt' 카테고리의 다른 글

nuxt + spring boot + jpa + h2 게시판 만들기  (0) 2023.05.09
반응형

npm 설치 되어있다는 가정 하에,

 

 

1. nuxt 프로젝트 생성

 

1) nuxt 프로젝트 시작할 폴더하나 만들기

2) cmd창 혹은 터미널에서 해당 폴더 경로로 이동하여 아래 명령어 실행

 

1. Vue-CLI, Vue-CLI-init설치

$ npm i -g @vue/cli
$ npm i -g @vue/cli-init

# 설치 후 버전 확인
$ vue --version

2. nuxt 프로젝트 생성

$ npm init nuxt-app <project-name>

#혹시 위 커맨드가 안된다면 아래 커맨드사용
$ vue init nuxt-community/starter-template myvueapp

<project-name> 대신에 본인이 원하는 프로젝트명으로 이름 넣어주면 된다

 

nuxt 프로젝트 생성완료

 

2. 프로젝트 실행

 

생성된 nuxt 프로젝트로 이동

cd <project-name>

프로젝트 실행

npm run dev

 

3. spring boot 프로젝트 생성

sts, intellij 에서 바로 만들어도되지만, 나는 https://start.spring.io/  여기를 이용했다.

jdk 17로 설정하고 디펜던시는 적당히 꾸려줬다

h2 안쓰면 추가할필요없음. 본인이 추가하고싶은 db 추가하면됨

 

1)기초 controller 생성

mainController 하나 생성해준다.

@RestController
@RequestMapping("/api")
public class MainController {

    @GetMapping(value="/helloWorld")
    public String helloWorld() {
        return "Hello World!";
    }

}

꼭... @RestController로 어노테이션 달아야한다.. @Controller로 달았다가 3시간동안 삽질함.

 

2) h2 연결

application.yml 수정해준다

server:
  port: 8081

spring:
  jackson:
    property-naming-strategy: SNAKE_CASE

  jpa:
    hibernate:
      ddl-auto: create-drop
    show-sql: true
    generate-ddl: true
    defer-datasource-initialization: true

  h2:
    console:
      enabled: true  # H2 Console을 사용할지 여부 (H2 Console은 H2 Database를 UI로 제공해주는 기능)
      path: /h2-console  # H2 Console의 Path
  # Database Setting Info (Database를 H2로 사용하기 위해 H2연결 정보 입력)
  datasource:
    driver-class-name: org.h2.Driver  # Database를 H2로 사용하겠다.
    url: jdbc:h2:~/test  # H2 접속 정보
    username: sa  # H2 접속 시 입력할 username 정보 (원하는 것으로 입력)
    password:  # H2 접속 시 입력할 password 정보 (원하는 것으로 입력)

포트설정 어쩌고저쩌고 하고 실습용으로 h2를 달아놓음

 

 

4. nuxt - spring proxy 연결

  - jsp로 만들지않는이상, html이나 vue로 만들게되면 프론트, 백단 서버를 따로따로 띄워야한다. 

   프론트서버 포트 3000, 백단 서버 8080 이런식으로 뜨게되는데, 프론트 포트3000에서 백단 8080을 호출해서 통신하는데 서로 포트가 다르면 cors error 발생하므로 연결해주는 작업이 필요하다

 

1) axios, proxy install

 

 axios : get/post 통신장치

$ npm install @nuxtjs/axios

 

proxy : 포트 간 통신을 위한 설정장치

$ npm install @nuxtjs/proxy

 

2) nuxt.config.js 수정

module.exports = {
telemetry:true,
...
modules: [
    '@nuxtjs/axios',
    '@nuxtjs/proxy'
  ],
  axios: {
    proxy: true
  },
  proxy: {
    '/api': {
      target: 'http://localhost:8443/', //spring port 입력
      changeOrigin: true // cross origin 허용
    }
  }
}

/api 로 시작되는 요청은 모두 8443 포트로 넘겨버리겠다는 설정이다.

 

 

3) 연결확인

- spring boot 프로젝트, nuxt 프로젝트 둘다 run 시킴

- nuxt프로젝트 localhost:3000 뜨면 localhost:3000/api/helloWorld 주소창에 적고 엔터!

- 프론트 화면에 백단에서 보낸 Hello World 글자 보이는지 확인

 

5. jpa 연결

1) 데이터 바인딩

 데이터 바인딩 되어야될 페이지에 mounted로 걸어준다

<template>
...
 	<tr v-for="(row, idx) in list" :key="idx">
          <td>{{ row.idx }}</td>
          <td><a v-on:click="fnView(`${row.idx}`)">{{ row.title }}</a></td>
          <td>{{ row.author }}</td>
          <td>{{ row.created_at }}</td>
	</tr>
</template>

<script>
export default {
    data() { //변수생성
        return {
            requestBody: {}, //리스트 페이지 데이터전송
            list: {}, //리스트 데이터
        }
    },
    mounted() {
        this.getBoardList()
    },
    methods: {
        getBoardList() {
            this.requestBody = { // 데이터 전송
                keyword: this.keyword,
                page: this.page,
                size: this.size
            }

            this.$axios.get(this.$serverUrl + "/board/list", {
                params: this.requestBody,
                headers: {}
            }).then((res) => {
                this.list = res.data  //서버에서 데이터를 목록으로 보내므로 바로 할당하여 사용할 수 있다.
            }).catch((err) => {
                alert(err.message);
            })
        }
    }
}
</script>

 

 

2) back 서비스 만듦

@Override
    public List<BoardDto> getBoardList() {
        List<BoardDto> result = new ArrayList<>();
        List<Board> boardEntities = boardRepository.findAll();

        for (Board entity : boardEntities) {
            BoardDto dto = BoardDto.builder()
                    .idx(entity.getIdx())
                    .author(entity.getAuthor())
                    .title(entity.getTitle())
                    .contents(entity.getContents())
                    .createdAt(entity.getCreatedAt().format(DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss")))
                    .build();

            result.add(dto);
        }

        return result;
    }

 

3) back api 만듦

@RestController
@RequiredArgsConstructor
public class BoardController {

    private final BoardService boardService;

    @ResponseBody
    @GetMapping("/board/list")
    public List<BoardDto> boardList() {
        return boardService.getBoardList();
    }
 }

 

4) 데이터 연결 확인

nuxt / vue 다른점

 

vue : 만약 vue 였다면 이 상황에서 게시글 상세페이지를 보고싶을때, 새로운 vue 페이지만들고 라우터를 추가해야함

nuxt : 라우터추가안해도됨, pages에 넣어두면 url을 통한 이동은 바로 이동됨

ex)

이런식으로 pages에 index2.vue라는 파일을 생성하고 링크로 이동하면 해당페이지에 바로 접속된다.

별도로 사용할 conponents는 해당폴더에 따로 만들어두고 사용하면됨

 

 

 

 

참고 url : https://onethejay.tistory.com/64, https://minu0807.tistory.com/65

728x90

'FrontEnd > Nuxt' 카테고리의 다른 글

nuxt js 페이지 이동, 레이아웃  (0) 2023.05.09
반응형

아래와 같은 관계도가 있는 테이블이 있다.

 

위에 그림 두개는 DB 관계도, 아래의 이미지는 자바단 부모 @Entity 쪽 @OneToMany 설정이다.

 

원래는 LAZY를 쓰고싶지만 회사 ..사정상 EAGER를 쓰고있다.

 

 

편의상 OP_REGULAR_ROUND => OP_ROUND

OP_USER_REGULAR_ROUND -> OP_ROUND_ORDER 로 바꿔서 설명하겠다.

 

1. JPA N+1 이슈

 

내가원하는 SQL문은 아래와 같다.

SELECT RR.ROUND, OR.*
FROM OP_ROUND OR
         INNER JOIN OP_ROUND_ORDER RR ON OR.CODE = RR.CODE
WHERE OR.CODE = '1111'
ORDER BY RR.ROUND DESC;

조인을 하고 코드를 파라미터로 넘겨준다.

 

1) service와 같은 root 에 repository를 생성 - 나는 일단 impl 생성했다.

public interface RoundCustom {
    /**
     * 설명 ~~~~
     * @return
     */
    Round getDetail(String code);
}
import static saleson.model.QRound.round;
import static saleson.model.QRoundOrder.roundOrder;

@RequiredArgsConstructor
public class RoundCustomImpl implements RoundCustom{
    private final JPAQueryFactory jpaQueryFactory;

    @Override
    public Round getDetail(String roundCode) {
    }
}

querydsl에서 사용할 q클래스와 JPAQeuryFactory 를 선언해줬다.

 

그리고 리포지토리에 extends 추가해준다.

사실 커스텀안하고 별도 리포지토리를 생성하고 거기에 관리해도된다.

public interface RoundRepository extends JpaRepository<Round, Long>, QuerydslPredicateExecutor<Round>, RoundCustom {
    Optional<Round> findByCode(String code);
}

 

 

2) QUERY DSL를 사용 할 메소드 생성

다시 RoundCustomImpl에 돌아와서..

public Round getDetail(String code) {
    
    //builder 를 따로 뺀 이유는, 이후에 where 조건이 추가될수도 있기 때문입니다.
    BooleanBuilder builder = new BooleanBuilder();
    builder.and(round.code.eq(code));

    }

querydsl 생성에 앞서 먼저 builder 를 만들어준다. 

 

where 조건에 들어갈 부분을 넣으면 된다.

 

public Round getDetail(String code) {
    
    //builder 를 따로 뺀 이유는, 이후에 where 조건이 추가될수도 있기 때문입니다.
    BooleanBuilder builder = new BooleanBuilder();
    builder.and(round.code.eq(code));

    return jpaQueryFactory
            .select(round)
            .from(round)
            .innerJoin(round.list,roundOrder) //oneToMany
            .fetchJoin()
            .where(builder)
            .orderBy(roundOrder.round.desc())
            .fetchOne();
    }

이게 완성된 로직인데, innerjoin에 들어가는 파라미터는, 부모@Entity에 @OneToMany로 걸려있는 변수를, 뒤에는 조인할 q클래스명을 입력해줬다.

 

이렇게 돌리면

select regularrou0_.ID                         as id1_47_0_,
       userregula1_.ID                         as id1_60_1_,
       regularrou0_.CREATED                    as created2_47_0_,
       regularrou0_.CREATED_BY                 as created_3_47_0_,
       regularrou0_.UPDATED                    as updated4_47_0_,
       regularrou0_.UPDATED_BY                 as updated_5_47_0_,
       regularrou0_.BUYER_EMAIL                as buyer_em6_47_0_,
       regularrou0_.BUYER_MOBILE               as buyer_mo7_47_0_,
       regularrou0_.BUYER_NAME                 as buyer_na8_47_0_,
       regularrou0_.CARD_NAME                  as card_nam9_47_0_,
       regularrou0_.INDEX_ROUND                as index_r10_47_0_,
       regularrou0_.ITEM_NAME                  as item_na11_47_0_,
       regularrou0_.ITEM_USER_CODE             as item_us12_47_0_,
       regularrou0_.RECEIVE_NAME               as receive13_47_0_,
       regularrou0_.REGULAR_ROUND_END_DATE     as regular14_47_0_,
       regularrou0_.REGULAR_ROUND_START_DATE   as regular15_47_0_,
       regularrou0_.STATUS                     as status16_47_0_,
       regularrou0_.TOTAL_ROUND                as total_r17_47_0_,
       regularrou0_.USER_ID                    as user_id18_47_0_,
       regularrou0_.USER_REGULAR_CARD_ID       as user_re19_47_0_,
       regularrou0_.USER_REGULAR_ROUND_CODE    as user_re20_47_0_,
       userregula1_.CREATED                    as created2_60_1_,
       userregula1_.CREATED_BY                 as created_3_60_1_,
       userregula1_.UPDATED                    as updated4_60_1_,
       userregula1_.UPDATED_BY                 as updated_5_60_1_,
       userregula1_.DISCOUNT_RATE              as discount6_60_1_,
       userregula1_.FAIL_COUNT                 as fail_cou7_60_1_,
       userregula1_.ITEM_ID                    as item_id8_60_1_,
       userregula1_.ITEM_NAME                  as item_nam9_60_1_,
       userregula1_.ITEM_SEQUENCE              as item_se10_60_1_,
       userregula1_.ORDER_CODE                 as order_c11_60_1_,
       userregula1_.ORDER_DATE                 as order_d12_60_1_,
       userregula1_.ORDER_SEQUENCE             as order_s13_60_1_,
       userregula1_.ORDER_STATUS               as order_s14_60_1_,
       userregula1_.PAY_AMOUNT                 as pay_amo15_60_1_,
       userregula1_.PAY_DATE                   as pay_dat16_60_1_,
       userregula1_.PAYMENT_INTERVAL           as payment17_60_1_,
       userregula1_.QUANTITY                   as quantit18_60_1_,
       userregula1_.ROUND                      as round19_60_1_,
       userregula1_.SHIPPING_CANCEL_END_DATE   as shippin20_60_1_,
       userregula1_.SHIPPING_CANCEL_START_DATE as shippin21_60_1_,
       userregula1_.SHIPPING_READY_END_DATE    as shippin22_60_1_,
       userregula1_.SHIPPING_READY_START_DATE  as shippin23_60_1_,
       userregula1_.USE_FLAG                   as use_fla24_60_1_,
       userregula1_.USER_DELIVERY_ID           as user_de25_60_1_,
       userregula1_.USER_ID                    as user_id26_60_1_,
       userregula1_.USER_REGULAR_CARD_ID       as user_re27_60_1_,
       userregula1_.USER_REGULAR_ROUND_CODE    as user_re28_60_1_,
       userregula1_.USER_REGULAR_ROUND_CODE    as user_re28_60_0__,
       userregula1_.ID                         as id1_60_0__
from OP_ROUND regularrou0_
         inner join OP_ROUND_ORDER userregula1_
                    on regularrou0_.CODE = userregula1_.code
where regularrou0_.CODE = '1111'
order by userregula1_.ROUND desc

 

이런식으로 내가원하던 조인문이 나온다.

 

 

이때 fetchJoin을 안쓰면

select regularrou0_.ID                       as id1_47_,
       regularrou0_.CREATED                  as created2_47_,
       regularrou0_.CREATED_BY               as created_3_47_,
       regularrou0_.UPDATED                  as updated4_47_,
       regularrou0_.UPDATED_BY               as updated_5_47_,
       regularrou0_.BUYER_EMAIL              as buyer_em6_47_,
       regularrou0_.BUYER_MOBILE             as buyer_mo7_47_,
       regularrou0_.BUYER_NAME               as buyer_na8_47_,
       regularrou0_.CARD_NAME                as card_nam9_47_,
       regularrou0_.INDEX_ROUND              as index_r10_47_,
       regularrou0_.ITEM_NAME                as item_na11_47_,
       regularrou0_.ITEM_USER_CODE           as item_us12_47_,
       regularrou0_.RECEIVE_NAME             as receive13_47_,
       regularrou0_.REGULAR_ROUND_END_DATE   as regular14_47_,
       regularrou0_.REGULAR_ROUND_START_DATE as regular15_47_,
       regularrou0_.STATUS                   as status16_47_,
       regularrou0_.TOTAL_ROUND              as total_r17_47_,
       regularrou0_.USER_ID                  as user_id18_47_,
       regularrou0_.USER_REGULAR_CARD_ID     as user_re19_47_,
       regularrou0_.USER_REGULAR_ROUND_CODE  as user_re20_47_
from OP_ROUND regularrou0_
         inner join OP_ROUND_ORDER userregula1_
                    on (regularrou0_.CODE = userregula1_.CODE)
where regularrou0_.CODE = '1111'
order by userregula1_.ROUND desc;

select userregula0_.USER_REGULAR_ROUND_CODE    as user_re28_60_0_,
       userregula0_.ID                         as id1_60_0_,
       userregula0_.ID                         as id1_60_1_,
       userregula0_.CREATED                    as created2_60_1_,
       userregula0_.CREATED_BY                 as created_3_60_1_,
       userregula0_.UPDATED                    as updated4_60_1_,
       userregula0_.UPDATED_BY                 as updated_5_60_1_,
       userregula0_.DISCOUNT_RATE              as discount6_60_1_,
       userregula0_.FAIL_COUNT                 as fail_cou7_60_1_,
       userregula0_.ITEM_ID                    as item_id8_60_1_,
       userregula0_.ITEM_NAME                  as item_nam9_60_1_,
       userregula0_.ITEM_SEQUENCE              as item_se10_60_1_,
       userregula0_.ORDER_CODE                 as order_c11_60_1_,
       userregula0_.ORDER_DATE                 as order_d12_60_1_,
       userregula0_.ORDER_SEQUENCE             as order_s13_60_1_,
       userregula0_.ORDER_STATUS               as order_s14_60_1_,
       userregula0_.PAY_AMOUNT                 as pay_amo15_60_1_,
       userregula0_.PAY_DATE                   as pay_dat16_60_1_,
       userregula0_.PAYMENT_INTERVAL           as payment17_60_1_,
       userregula0_.QUANTITY                   as quantit18_60_1_,
       userregula0_.ROUND                      as round19_60_1_,
       userregula0_.SHIPPING_CANCEL_END_DATE   as shippin20_60_1_,
       userregula0_.SHIPPING_CANCEL_START_DATE as shippin21_60_1_,
       userregula0_.SHIPPING_READY_END_DATE    as shippin22_60_1_,
       userregula0_.SHIPPING_READY_START_DATE  as shippin23_60_1_,
       userregula0_.USE_FLAG                   as use_fla24_60_1_,
       userregula0_.USER_DELIVERY_ID           as user_de25_60_1_,
       userregula0_.USER_ID                    as user_id26_60_1_,
       userregula0_.USER_REGULAR_CARD_ID       as user_re27_60_1_,
       userregula0_.USER_REGULAR_ROUND_CODE    as user_re28_60_1_
from OP_ROUND_ORDER userregula0_
where userregula0_.CODE = 'R20230216100309';

 

이런식으로 두번나온다.

 

하지만 fetchJoin이 만사형통은 아니다.. 페이징은 쓸수없기에....

 

참고로 해당 sql문은 콘솔에서 확인할수있는데

이런식으로 쓱- 보고 지나가기 쉽다. 하지만 꼭 작업할때 긁어서 실제로 sql문을 육안으로 확인해보는 습관을 길러야한다.

 

2. querydsl 결과를 특정 DTO에 리턴, 서브쿼리받는법

 

내가 받고싶은 sql문

select RR.USER_ID,
       RR.USER_REGULAR_ROUND_CODE,
       RR.ITEM_NAME,
       RR.REGULAR_ROUND_START_DATE,
       RR.TOTAL_ROUND,
       RR.BUYER_NAME,
       RR.CREATED,
       min(OURR.PAY_DATE),
       min(OURR.PAYMENT_INTERVAL),
       OURR.SHIPPING_READY_START_DATE
from OP_ROUND RR
inner join OP_ROUND_ORDER OURR
        on RR.CODE = OURR.CODE
where OURR.ORDER_CODE is null
  and RR.CODE = (select CODE
                  from OP_ROUND_ORDER
                  where ORDER_CODE = '2222')

 

특정 결과만 dto에 리턴받고싶을때

 

@Override
    public dtoA method2(String orderCode){

		//서브쿼리에 넣을 where 절
        BooleanBuilder subBuilder = new BooleanBuilder();
        subBuilder.and(roundOrder.orderCode.eq(orderCode));

		//메인쿼리에 넣을 where 절
        BooleanBuilder builder = new BooleanBuilder();
        builder.and(roundOrder.orderCode.isNull());
        builder.and(round.code.eq(
                JPAExpressions.select(roundOrder.code)
                        .from(roundOrder)
                        .where(subBuilder)
        ));

        return jpaQueryFactory
                .select(Projections.constructor(dtoA.class,
                        round.userId,
                        round.userRegularRoundCode,
                        round.itemName,
                        round.regularRoundStartDate,
                        round.totalRound,
                        round.buyerName,
                        round.created,
                        roundOrder.payDate.min(),
                        roundOrder.paymentInterval.min())) //그룹함수는 여기서
                .from(round)
                .join(round.list,roundOrder)
                .where(builder)
                .fetchOne();
    }

서브쿼리는 JPAExpressions 를 사용해서 조인시킨다.

역시 이때도 where 조건이 많아지면 너무 산만하기때문에 상단에 따로 선언하고 사용하였다.

 

Projections.contructor 사용하면 dto로 받을수있다.

첫 파라미터에 리턴받고싶은 dto 클래스를 추가하고 뒤에는 전부 내가받고싶은 컬럼명 나열한다.

 

이때 해당 dto에 해당 컬럼을 받는 생성자가 선언되어있어야한다.

그룹함수는 select할때 바로 쓰면된다.

 

728x90

+ Recent posts