오늘 내용의 샘플 코드는 다음 깃허브 저장소를 통해서 확인가능합니다.
소스코드 확인하기
앵귤러 + 스프링부트 + RDBMS
우리에게 상당히 친숙한 3-tier 아키텍처입니다.
제가 현재 속한 팀에서도 많이 사용하는 데요.
우리가 매번 엄청난 트래픽이 예상되는 서비스를 만들지는 않습니다.
낮은 워크로드를 요구하는 어플리케이션의 경우 앵귤러와 스프링부트를 함께 패키징해서 사용하기도 합니다.
제가 지금까지는 패키징할때마다 엄청난 시간을 소모하는데요. (게을러서 Gradle 스크립트를 제대로 공부하지 않아서...)
그래서 이번에는 아예 샘플로 사용할 프로젝트를 만들어버렸습니다. (물론 공부도 했지요 ㅎ )
( 깃허브 저장소를 참고하세요 )
오늘은 그레이들을 활용하여 스프링부트와 앵귤러를 함께 패키징하는 방법에 대해 이야기 해보도록 하겠습니다.
TABLE OF CONTENTS 목차
- Dependencies
- 원리알기
- 스프링부트의 static 폴더와 GET
- Angular의 build
- 1번과 2번을 연결해서 생각해보면?
- 그레이들의 bootJar
- 구현하기
- 내부 구조 설명 및 구현
- build.gradle 파일 작성
- 빌드 및 실행
- 추가 학습 경로마치며...
1 Dependencies
이번 프로젝트를 위해 사용된 프로그램 버전입니다.
NPM 6.14.4
Node 12.19.1
Java 11
Gradle 7.1.1
2 원리알기
2 - 1 스프링부트의 static 폴더와 GET
우리가 브라우저에 www.naver.com이라고 입력해서 엔터를 치면 어떻게 되나요?
당연히 네이버 웹사이트로 이동합니다!
저는 내부적으로 발생하는 것을 이야기 해보겠습니다.
위의 경우 스프링부트 어플리케이션은 여기서 무슨 역할을 수행할까요?
- 우리 노트북에 설치된 브라우저로 위 주소로 GET 요청을 보냅니다.
- 서버에서 요청을 받는다.
- ***대다수의 웹사이트의 랜딩페이지는 index.html 입니다.
- 서버에서 www.naver.com 과 www.naver.com/index.html 은 대부분의 경우 같은 경로로 인식합니다.
- src > main> resources > static 폴더에 위치한 index.html 파일을 찾습니다.
- 그리고 index.html 라는 정적자원을 사용자에게 전송합니다.
- index.html 파일이 있다면 네이버 페이지를 보게 될것이고 없다면
- 404 응답을(https://en.wikipedia.org/wiki/HTTP_404)를 반환합니다.
*** TIP ***
만약 우리 스프링부트 앱의 주소가 http://kimjoohyuk.com 이고 스프링부트 앱 /static 폴더에 index.html 가 존재한다면
index.html을 웰컴 페이지로 매핑하여 전송합니다.
2-2 Angular의 build
이제 설명해드릴 부분은 SPA (Single Page Application) 프레임워크인 앵귤러, 리액트, 뷰 모두 적용되는 부분입니다.
우리는 프론트엔드 코드를 작성하고 빌드를 합니다.
npm run build
그러면 npm 노드 패키지 매니저는
- npm은 코드와 이미지 모듈 등의 자원들을 한데 모아서
- 바이트코드와 같은 소스 압축 과정을 거친 다음 (보통 dist폴더) 에 가져다 둡니다.
- 이름모를 js , css 파일들이 있는데 index.html 파일이 나옵니다.
- 들여다보면 말그대로 싱글페이지가 반환되는군요. 허rrrrrr......
***TIP***
index.html 은 프론트엔드 어플리케이션의 실행 시작점 즉, 엔트리 포인트라고 생각하시면 됩니다.
스프링 어플리케이션의 SpringApplication.run 메소드처럼 말이지요.
SpringApplication.run(BackendApplication.class, args);
2-3 : 1번과 2번을 연결해서 생각해보면?
이제 이해가 가지 않습니까?
만약 저 dist 폴더 내부의 파일들이(2-2참고).... 스프링부트에 static 폴더 내부에 있다면(2-1참고)...
사용자가 www.naver.com 음.. 어... 그리고 한번에 빌드?
네 맞습니다!
이제 그레이들을 어떻게 활용할지 감이 잡히시죠?
2-4 : 그레이들
코드를 보기전 마지막 설명입니다.
프로젝트 빌드의 라이프사이클을 생각해봅시다.
코드 작성, 테스트, 빌드, 등.... ( 네, 매우 짧은 생각이었습니다. )
그레이들도 npm 처럼 프로젝트를 빌드를 도와줍니다.
Gradle 공식 홈페이지에 대문짝만하게 "Gradle Build Tool" 빌드 툴이라고 써놓았습니다.
npm run build 커맨드와 대칭하는 gradle build 커맨드가 있습니다.
우리가 프로젝트 루트 디렉토리에서 gradle build 를 실행하면
그레이들은 필요한 자원들을 모아서 jar 파일. 스프링부트를 다 만들고 빌드 및 배포를 하기 위해서 .jar 이나 .war 파일을 만들지요.
그레이들 빌드의 결과물은 build 폴더에서
코드를 찾아서 테스트를 하고 빌드를 합니다.
그리고 우리에게 필요한, 그리고 앞으로 구현할 그레이들의 빌드 프로세스는 다음과 같습니다.
- gradle build 실행
- npm run build 실행 (앵귤러 어플리케이션 빌드)
- npm 빌드 아웃풋을 찾아서 (index.html과 여러 정적자원들)
- 스프링부트의 static 폴더에 가져다 놓습니다.
- 스프링 프로젝트 빌드
- build 폴더 -> libs 폴더 내부에 .jar 파일 빌드 완료!
그럼 이제 구현 시작하시죠.
3 구현하기
3-1 내부 구조 설정
- 우리가 목표 하는 우리의 프로젝트 구조 (사진참고) 설정 순서는 다음과 같습니다.
- 기본 스프링부트 프로젝트를 생성한다.
- 스프링프로젝트 내부에서 demo-ui 앵귤러 프로젝트를 생성한다. ( 스프링프로젝트 내부에서 "ng new demo-ui" 커맨드 실행)
- demo-ui 디렉토리 하위에 있는 angular.json 과 package.json 파일을 프로젝트 루트 디렉토리로 이동시킵니다.
- angular.json 파일을 수정하여 프로젝트 자원 경로 설정을 변경해줍니다. 우리가 변경해 주어야 할 곳은 앵귤러 프로젝트가 상대경로를 통해 자원의 위치를 찾는 부분들입니다.
- (변경할 부분은 아래 슬라이드쇼의 하이라이트 된 구간들을 참고해서 빠짐없이 해줍니다.)012
- (변경할 부분은 아래 슬라이드쇼의 하이라이트 된 구간들을 참고해서 빠짐없이 해줍니다.)
- 그럼 이제 첫 단계는 마무리가 되었습니다.
3-2 build.gradle 파일 작성
우리가 빌드 프로세스를 다룰수 있게하는 build.gradle 파일을 작성할 것입니다.
build.gradle 파일은 스프링부트 프로젝트 생성시에 gradle 옵션을 선택해주시면 자동 생성됩니다. (미리 이야기를 했어야...)
다음은 우리가 작성해야할 build.gradle 파일이며 설명은 다음과 같으며 사진을 참고해서 이해하시면 좋을 것 같습니다.
- 그레이들에서 npm 커맨드를 실행할 수 있는 노드 그레이들 플러그인을 설치합니다.
- node 와 npm 의 버전을 아래와 같이 명시적으로 설정할 수도 있으나 참고하시고 여기서는 생략하겠습니다.
node { /// 설정정보 version = 12.1.0 }
- 설치한 플러그인을 적용합니다.
- npm 빌드 프로세스를 스프링부트 빌드 프로세스에 연결하기
- 그레이들의 dependsOn 의존관계를 활용해서 프로세스 진행 순서를 명시 npmInstall >>> buildFrontend >>> bootJar
- 빌드 아웃풋 jar 파일의 이름은 "ExampleApplication.jar"입니다. 빌드 중 bootJar(= jar 파일 생성) 과정에서 demo/dist 디렉토리의 파일들을 스프링부트의 static 디렉토리에 복사합니다.
- npm run build 커맨드를 실행시킵니다.
***TIP***
- bootJar 은 그레이들 빌드 라이프 사이클 중 한 구간입니다.
- gradle build 실행시 bootJar 태스크가 자동으로 바인딩됩니다.
- buildFrontend 태스크는 우리가 설치한 플러그인에서 기본 제공하는 npmInstall 을 의존하게하여
- 3-1번을 자세히 설명하자면
- buildFrontend 태스크 실행전에 npm install 커맨드를 먼저 실행합니다.
- bootJar은 그 위에 작성한 buildFrontend 태스크에 dependsOn = 의존하게 작성되었습니다.
- jar파일을 빌드하는데 있습니다.
3-3 : 빌드 및 실행
자 이제 그레이들을 사용해서 빌드를 해보겠습니다.
성공적으로 빌드가 완료되면 프로젝트 디렉토리 > build > libs 안에 ExampleApplication.jar 파일이 보입니다.
이동 후 .jar 파일을 실행해 보겠습니다.
java -jar ExampleApplication.jar
그러면 아래와 같이 친숙한 스프링부트의 로그가 찍히며 (아래 사진의 ) 빨간 밑줄 부분을 보면 welcome 페이지가 추가된 것을 확인할 수있습니다. 참고로 저는 스프링부트 프로젝트 생성 이후 build.gradle 파일 외에는 만진 것이 없습니다.
자 그럼 이제 처음에 설명드렸던 www.naver.com으로 접속해보겠습니다.
여기서는 localhost:8080이 되겠군요.
굿
이렇게 spring-meets-angular 프로젝트, 스프링부트와 앵귤러 프로젝트를 연동하는 작업을 함께 진행해 보았습니다.
4 추가 학습 경로
이번에 우리는 많은 것을 배웠지만, 또 많은 배울 것들이 남아있습니다.
이번 프로젝트 이후 추가적으로 파고들만한 것들을 한번 생각해볼까요?
- 그레이들의 멀티 모듈 프로젝트
- 우리는 프로젝트의 범위를 간소화 하기위해 Angular 어플리케이션을 SpringBoot 내부에 embed 하였습니다. 이것을 그레이들의 관점에서는 build.gradle 파일이 1개인 1개의 모듈을 빌드했다고 생각하셔도 좋습니다.
- 그렇다면 build.gradle 파일이 2개라면? 모듈이 2개인 멀티 모듈이 되겠네요. 멀티 모듈이라는 키워드에서 우리는 다른 구조, 다른 빌드 구조를 생각해 볼 수 있습니다. 예를들어.... 비어있는 루트 프로젝트를 두고 그 하위에 스프링부트와 앵귤러 프로젝트를 각 backend 와 frontend 디렉토리에 넣는 방법처럼 말이지요. 충분히 가능한 방법이며 이또한 많이 쓰이는 방법이니 도전해보셔도 좋습니다. (성공 후 공유 부탁드립니다!)
- 환경 environment 별 설정
- 현재 우리 프로젝트는 dev prod local 환경 모두 같은 빌드 프로세스를 따르고 있습니다.
- 우리에게 필요한 것은 npm run build -prod 에서 prod와 같은 정보를 스프링부트와 앵귤러 모두에게 주입시키는 방법을 찾아야합니다. 그렇지 않으면 개발시에 프로덕션 빌드를 강행하는 무거운, 매우 비효율적인 프로젝트로 남게 될 것입니다.
- 스프링부트와 앵귤러 프로젝트 REST API 로 소통하기!
- 빌드시에는 같은 파일에 있지만 애플리케이션 운용시에 앵귤러 어플리케이션은 사용자 브라우저에서 실행되며 스프링부트 서버는 회사 또는 클라우드 환경에서 사용자의 요청을 기다리고 있습니다.
- 그렇다면 우리는 사용자 브라우저와 서버의 통신을 할 수 있게 해야겠지요?
- 여기서 이제 스프링부트의 기술들을 활용하게 됩니다.
- REST API 로 소통을 하게되는 것이지요.
- 데이터 베이스 연동
- 사용자가 서비스를 사용하며 발생하는 데이터 중 보존이 필요한 것이있습니다. 아이디, 유저 게시물 등이 있겠지요.
- 그렇다면 이 정보는 어디에 저장하면 좋을까요? 데이터베이스입니다.
- 데이터베이스에도 다양한 종류가 있으니 데이터 베이스 연동도 3번과 함께 시도해보시면 좋을 것 같습니다.
- 저는 김주혁이라고 합니다.
- 블로깅은 자주 접하지 않아서 이번 게시물을 퇴근 후 4시간 넘게 작성하고 있네요.
- 이런 노력을 알아주시고 저에 대해서도 알아주시길 부탁드립니다.
- 아래 정보를 참고해서요 ㅎ
*** URL을 클릭해서 이동하세요 ***
- 🌱 Blog (Medium) : https://medium.com/@beanskobe
- 🌱 Blog (티스토리) : https://vince-kim.tistory.com/
- 📫 Portfolio (포트폴리오) : https://romantic-golick-a520aa.netlify.app
- 📫 Website (웹사이트) : https://romantic-golick-a520aa.netlify.app
- ✏️ LinkedIn (링크드인) : https://www.linkedin.com/in/joo-hyuk-kim/
- 🌎 Contact me : beanskobe@gmail.com
- 🌎 Gmail : beanskobe@gmail.com