급하게 회사에서 외주 프로젝트가 들어왔다. 한번쯤은 스프링으로 해보는 것도 나쁘지 않다고 생각했는데... 너무 두렵다... 아무튼 예전에 egov랑 스프링을 조금 해본 정도로 프로젝트를 진행한다. 개념은 아예 없기 때문에 블로그 쓰면서 스터디 하면서 정리할 예정이다. 너무 쉽게 생각했다...
IDE
우선 IDE인데 이클립스를 하려고 했는데 나의 상황이 사용중인 IDE가 안드로이드 스튜디오(안드로이드), VS Code(Node.js) 인데 스프링 하려고 이클립스까지 쓰면 IDE는 총 3개 사용하는 것이다. 아마 PC 바꾸면 대참사가 날 것이다. 그래서 IntelliJ IDE를 사용해보려고 한다. 아마 자기가 학생이면 잘 모를수도 있다. IntelliJ도 학생은 무료로 사용할 수 있다. 그렇기 때문에 한번 쯤 써보는 것도 좋다. 굉장히 많은 툴들을 지원하고 JAVA, PHP, Node.js, Android...등 많은 언어들을 지원하기 때문에 나로썬 써보는 게 좋다고 생각했다. 그래서 대부분 IDE를 IntelliJ로 진행해보려한다.
프로젝트 세팅은 스프링 페이지에서 만들어서 IDE에 Import하는 방법과 IDE에서 생성하는 방법이 있다. 물론 두 방법 다 과정은 같기 때문에 IDE에서 생성해보려고 한다.
1. New > project > New Project 에서 왼쪽 탭에 Spring Initalizr을 누르면 위 이미지처럼 나온다. 물론 탭에 없다면 맨 아래 Empty Project에서 찾아보기 바란다...
2. 스프링 프로젝트의 세팅 - Group과 Artifact, package name 등을 입력하고 빌드도구랑 언어는 우선은 Maven, Java를 선택
패키징은Jar와 War중선택이 가능한데 Jar를 선택하면 스프링부트내장톰캣(Embeded Tomcat)을 사용하여 Stand Alone으로 WAS를 구동시키고. 외부 WAS에 Deploy해서 사용하는 환경이시라면 War를 선택하여 내보내면 된단다...
3. Next를 눌러서 다음으로 DI 세팅이다. 내가 필요한 DI를 선택하면 빌드에 자동으로 항목이 추가되고 리포지토리에서 내려받아진다. 물론 나중에 따로 추가도 가능하다.
4. Finish를 누르면 끝난다. 생성된 스프링부트 프로젝트의 구조인데 주요 파일로는 스프링부트 컨테이너를 실행시키기 위한 src폴더내의main가 있고 resources 폴더에는 web을 선택했기 때문에 html, css, js 등의 정적 컨텐츠가 위치할static 폴더가 자동으로 생성됐으며 여러가지 설정 정보를 위한application.properties파일도 생성됐다.
5. 우선 국룰을 통해서 프로젝트를 만들었으면 실행을 해야한다고 생각한다. 이클립스 자바 할 때처럼 Run을 해보자...당연히 바로 안된다.
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled. 2021-06-25 16:08:48.400 ERROR 238940 --- [ restartedMain] o.s.b.d.LoggingFailureAnalysisReporter :
*************************** APPLICATION FAILED TO START ***************************
Description:
Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.
Reason: Failed to determine a suitable driver class
Action:
Consider the following: If you want an embedded database (H2, HSQL or Derby), please put it on the classpath. If you have database settings to be loaded from a particular profile you may need to activate it (no profiles are currently active).
Process finished with exit code 0
뭐라고 주절주절 하는데 아래 Action: 을 보면 뭐라 써있다. 대충 읽어보면 아까 DI 세팅할 때 DB를 같이 추가 했는데 DB에 대한 기본 설정을 하지 않아 발생한 문제다.
2가지 방법으로 해결했다.
5-1 application.properties에 DB 세팅을 해준다. 자신의 설정에 맞게 해주면 된다.
5-2 우선 어노테이션을 통해 Auto Configuration에서 DataSource 관련 설정 로드를 WAS 구동시 하지 않도록 exclude하는 방법이 있다.
@SpringBootApplication(exclude={DataSourceAutoConfiguration.class}) (붙여서 하면 자꾸 클래스 부분이 주석처리 되어 에러나니깐 @SpringBootApplication을 엔터 몇번 치고 복붙하자)
6. 그러면 이제 세팅이 완료 됐으면 빌드 후 다시 실행해보면 정상적으로 작동한다. 맨 아래 Started DemoApplication in 2.162 seconds (JVM running for 3.37) 나온다면 정상 작동을 한 것이다.
7. 그럼 정말 정상 작동하는지 브라우저에서 확인을 해봐야 하지 않나... 우선 index.html을 간단하게 만들어서 테스트해보자.
리소스 정적 폴더에 index.html을 만들면 바로 intellij에서는 해당하는 html 파일을 바로 브라우저를 킬 수 있게 되어 있지만 브라우저에서 https://localhost:8080에 접속하면 index.html에 작성한 페이지가 나온다. 물론 internet explorer은 https가 안된다. 주의하자!
이 포스팅에선 postman을 이용하여 업로드를 진행할 것이다. 추후에 안드로이드 어플을 통해서 업로드 Client 부분을 작성할 예정이며 작성하면 이 글을 수정하여 링크를 달아놓을 예정이다.
우선 파일을 업로드하여 서버 upload라는 폴더 안에 저장할 것이고, DB에 파일에 대한 정보를 넣을 생각이다. 이 이유는 맨 아래에 남겨놨다.
우선 NPM 에서 사용하는 기본 예제다.
var express = require('express')
var multer = require('multer')
var upload = multer({ dest: 'uploads/' })
var app = express()
app.post('/profile', upload.single('avatar'), function (req, res, next) {
// req.file is the `avatar` file
// req.body will hold the text fields, if there were any
})
app.post('/photos/upload', upload.array('photos', 12), function (req, res, next) {
// req.files is array of `photos` files
// req.body will contain the text fields, if there were any
})
var cpUpload = upload.fields([{ name: 'avatar', maxCount: 1 }, { name: 'gallery', maxCount: 8 }])
app.post('/cool-profile', cpUpload, function (req, res, next) {
// req.files is an object (String -> Array) where fieldname is the key, and the value is array of files
//
// e.g.
// req.files['avatar'][0] -> File
// req.files['gallery'] -> Array
//
// req.body will contain the text fields, if there were any
})
코드에서 보면 알 수 있듯이 upload할 때 속성이 다르다.
.single(fieldname)
이름을 가진 단일 파일을 허용합니다fieldname.단일 파일은req.file. 로 사용하면 된다.
.array(fieldname[, maxCount])
이름이 모두 인 파일 배열을 허용합니다fieldname.둘 이상의maxCount파일이 업로드되면 선택적으로 오류가 발생합니다.파일 배열은req.files. 로 사용하면 된다.
.fields(fields)
fields 파라미터로 넘어오는 것들을 배열 형태로 저장된다. 파일은 req.files로 사용하면 된다.
var storage = multer.memoryStorage()
var upload = multer({ storage: storage })
파일 정보를 DB에 저장해둬야 할 필요가 있다. 왜냐면 파일을 다운 받을 경우 자신이 업로드한 파일을 정확하게 받을 수 있다. 그리고 파일 이름은 한글로 넣을 경우 깨지는 경우가 발생했다. UTF-8 등 ... 인코딩 많은걸 해봤지만 안됐다. 나중에 듣기론 이런 경우가 많아 파일 오리지널 이름을 같이 업로드하여 DB에 넣어둔 뒤 다운로드 할 시 DB에서 파일 이름을 꺼내와 다시 붙여주는 걸로 알고 있다. 물론 파일 리스트 조회 할 시도 DB에서 파일 오리지널 이름을 가져와 Client로 넘겨준다.
지금까지C나 JAVA에서는 include나 import를 하면 그 클래스나 함수, 변수들을 사용할 수 있었다. Node에선 require를 해도 그 변수나 함수를 사용하지 못했다.
Node에서는 모듈에서 exports를 하지 않으면 그 변수나 함수를 사용하지 못하는 것이다.
구글링으로 알아본 결과 A라는 모듈을 require를 하면 해당 모듈의 module.exports를 return 한다고 보면 되는 것이다. 그러므로 A모듈에서 module.exports를 하지 않았다면 그 어떤 것도 참조할 수 가 없다.
아무튼 본론으로 넘어와서 module.exports를 사용함에 많은 방법이 있었다.
우선 module.exports와 exports가 있다. 이 둘의 차이점... 사실 없었다. 공식 문서를 보면 그저 module.exports의 더 짧은 버전... 즉 module 조차도 길었다고 생각했나보다... 아무튼 둘의 차이점은 성능이 아닌 그저 더 짧다는 것에 있다.
그렇다면 어떻게 사용하면 되는건가... 여기부터는 솔직히 쓰는 사람의 특성과 그 모듈의 특성에 따라 나뉘는 듯 하다.
아래 예를 들어 코드를 작성해봤다. 기호에 맞게 사용하면 될듯하다.
exports.func = function (){console.log('func')}
exports = {func:func} // 이 경우 따로 func 함수가 정의가 되어 있어야함.
exports = { func : function (){ console.log('func') } }
exports.a = b
대학교 이후로는 이클립스를 처음 써본다... 안드로이드도 예전에는 이클립스를 이용했지만 지금은 분리되어 더더욱 쓸일이 없었고 왠만한건 다 VSCODE를 사용하기 때문에 쓸 일이 없었다. 갑자기 스프링을 하게 되어서 급하게 설치를 하는데 그냥 남겨봤다. 이런건 패스해도 되는데...
그러면 exe 파일이 다운이 되면서 실행하면 된다. 실행을 하면 아래 그림 처럼 나올것이다.
우선 첫번째 Eclipse IDE for Java Developers 는 단순히 자바만 사용하는 개발자의 IDE 타입입니다. 그러나 저는 스프링을 사용해야해서 두번째꺼 Eclipse IDE for Enterprise Java and Web Developers 선택을 합니다.
이 다음부터는 설치 위치나 JDK 위치경로 정도만 설정하고 설치하면 됩니다.
설치가 다 되면 혹시나 한글이 깨질 걸 생각에서 인코딩만 해보자.
메뉴에서 Window > Preferences > general > workspace 로 들어가면 하단에 Text file encoding 을 Other : UTF-8로 바꾼다.
그 다음으로 General > Editors > Text Editors > Spelling 에서 하단에 Encoding 을 Other : UTF-8로 바꿔준다.