# 이 책은 코드로 배우는 스프링 웹 프로젝트를 기반으로 하고 있습니다.

강력추천하는 책 이오니 스프링을 배우실 분들은 꼭 사서 보시길 바랍니다.


1) 전에 빌더스 할때는 로그인 세션을 컨트롤 단에서 해결했다.

쿼리로 로그인 확인 - vo가 존재 할 경우 session에 값 부여...

당시에는 이렇게 하는게 최고로 생각했다.



문제는 이렇게 할 경우 세션여부를 view에서 일일히 확인해줘야 했다. 위에처럼.. nav 단에서 일일히 확인하는 식으로..



이번에는 인터셉터가 그 역할을 하는것으로 바꾸었다.




아래 친구가 컨트롤러 이다. DB에서 id, pwd로 vo 값을 가져온다. vo가 있어야 로그인이 되므로 null이면 종료시켜야한다.



아래 친구는 인터셉터이다. 위에 model로 건네 준 값을 통해서 session을 부여한다.(아직 작업중이라 뭔가 많다...)


인터셉터는 막 사용할 수 있지 않다. xml에다가 아래처럼 꼭 등록을 해줘야한다.

첫번째처럼 전체 경로에 따른 interceptor가 있을 수 있고 아니면 특정 경로에 맞추어서 가능하다.



근데 웃긴점은 잘못된 아이디 / 비밀번호의 경우도 세션이 넘어가진다는 점 ㅡ,.ㅡ;; res가 null로 나오는데도 불구하고 그렇게 작동되니 참으로 신기할 따름이다.


두번째로는 파일 업로드 이다.

나의 경우엔 이미지 게시판이 주 이므로 메인 페이지에서 modal창을 통해 파일 업로드를 하는 식으로 구성 하였다.

파일 업로드의 경우 신경써야할 것이 몇가지 있다.

1. DB 구성, 게시판 테이블, 파일 경로가 저장될 테이블을 잘 설정 해놔야 한다.

2. LAST_INSERT_ID(); : 1번의 연장선이다.


내가 파일을 업로드 한다면 DB에는 크게 2가지 작업이 진행된다. 하나는 게시글의 저장이고, 다른 하나는 파일경로의 저장이다.

즉 10번 게시글에 hoya.jpg 가 올라간다고 하면 이 두가지가 각각 나눠져서 진행이 된다. 그렇기에 파일경로가 저장되는 테이블의 경우

필수적으로 10번, 즉 게시물의 넘버링이 붙어야 한다.


빌더스 때는 이런 비슷한 상황을 트리거를 만들어서 해결 했으나 이번엔 LAST_INSERT_ID()를 통해 해결하였다.

(가장 최근이 insert된 auto_increment를 가져온다)



컨트롤러에서 mutipart로 값을 가져온다. 얘네는 util class에서 정제된다(경로생성이 됨)


그 다음에 서비스 단에서 create를 하고 난 다음에 addAttach를 해준다. 이때문에 위에 Transctional이 필요로 하다.

오늘 이 부분때문에 매우 오랫동안 헤맸다...


쿼리문과 실제 삽입이 된 결고



서비스 단에 트랜잭션을 걸기위에선 위에 bean을 꼭 등록 해줘야한다. 나같은 경우 책을 부분부분 보다 보니 설정이 곳곳이 비어 있어서

안되는 경우가 발생하였고 이걸 모른체 2시간을 해맸다. 


막상 적어보니 오늘 한거 엄청 없는거 같다...


원래 계획은 오늘 war빼서 도메인 등록하는 거였는데... 내일로 미뤄야겠다.






이번 프로젝트는 이미지 게시판을 중점적으로 사용해보기 위함이다.


현재는 로그인, 파일 업로드 까지 완료 되었고 이 정도 시점에서 전 프로젝트와 조금씩 비교해보려 한다.

(현플젝 : 친구야 / 이전플젝: 빌더스)


1. web.xml


기본적으로 이번 프로젝트는 최초 생성부터 Spring legacy를 사용했기 때문에

세팅이나 폴더 구조등에 대해 따로 신경 쓸 필요가 없었다.

전 프로젝트는 아예 웹 프로젝트를 메이븐으로 컨버팅 하다보니 폴더구조도 많이 이상하다.


web.xml에서 가장 큰 차이는

<url-pattern>/</url-pattern> 부분이다.


이번 플젝은 RestFul하게 만드는게 목표이므로 기존처럼 .do 로 안했다. 

(실제 웹사이트 중 .do로 불려지는 곳 있는데-가끔- 그럴땐 반갑다)


2. servlet.xml


차이가 많이 난다.

빌더스에선 xml을 하나만 해놨다.(시큐리티 제외) 그렇다 보니 DB 연결 관련된 설정 모두 다 같이 모여있다.

반면에 친구야 에선 root와 servlet을 따로 구분 해놨다.

(빌더스는 root가 없다..)


3. User 컨트롤러

(좌 : 친구야 / 우 : 빌더스)



가장 큰 차이는 일단 logger의 사용.. 오른쪽만 하더라도 syso로 뽑는게 편했따.

물론 지금도 logger를 자유자재로 사용하지는 않고 단지 등록해서 쓰는 정도이지만.

빌더스 할 당시에는 로거 설정 자체를 만드는게 되게 힘들어서 나중에 겨우 하긴 했지만 만족스럽지는 못했다.


두번째로는 URI

이번엔 RestFul 하게 가기 위해 최대한 명사형태로, 간단하게 구성하려 노력중에 있다.

개발자가 제일 어려운 부분이 이름 짓는거라고 하는데 URI가 그러하다. 생각보다 명사, 복수형, 자원을 나타내는.. 식으로 만드는게 쉽지가 않다.


이번 프로젝트는 진행하면서 좀 헷갈리는 부분이 많이 있는데

그중 하나가 @Inject, @autowired다.


빌더스 할때는 무조건 @autowired를 썻다. 이번에는 @inject를 쓴다.

둘다 같은 역할이지만 인젝트는 자바 자체, auto는 스프링에서 제공하는 것이기 때문에

스프링에 종속적이지 않기 위해선 인젝트를 쓰라고 되어있다.


그런데 스프링에 종속적이지 않다는 것은 무슨 말일까

단순히 스프링 지원 말고 다른 프레임 워크에서도 사용가능하다 로 이해하면 될듯 하다.


stackoverflow를 찾아보니 역시나 비슷한 질문이 많다.

결론적으로 보자면 가장 큰 차이는 @required이다.


https://www.linkedin.com/pulse/difference-between-inject-vs-autowire-resource-pankaj-kumar

여기서 설명이 잘 되어있는데 @inject는 만일 프로퍼티가 없을경우 에러를 발생하지만 @autowired는 null로 인식하고 넘긴다고 한다.


One of the differences between @Autowired and @Inject is that @Inject does not have the required field so in case we fail to find a suitable object to injected it will fail while @Autowired can used required=false and allow null able field (only if required!).







컨트롤러 테스트를 위해


mockMvc를 연습 하던 중 만났던 문제이다.


@WebAppConfiguration 

test 클래스 위에 작성 되야하는 위 어노테이션이 임포트가 안되고

mockMvc 역시 임포트가 안되는 불상사...


m2를 날리고 update를 다 해봐도 안붙길래 겸사겸사 오늘접어야겠다 라고 마음 먹었다.


마지막 확인을 위해 porm.xml을 확인 하였는데


스프링 버젼이 3.1.1로 되어있었다...

아마도 지난번 mybatis 연결 테스트를 하면서 바뀐것 같은데..

ㅠㅠ 두시간을 날렸따...



2번째 프로젝트는 그냥.. 이것저것 해보려는 시도!!


3번째 프로젝트는 운동하자 친구야...


큰 기능 없이 단순히

운동 인증 / 나이대별 경쟁 / 댓글  정도의 간략한 기능만을 추가해서 

내 친구들에게 뿌릴 예정...


ㅎㅎ.. 게시판 + 파일 업로드 기능이 핵심이 되기때문에 기본기로 하기에 매우 좋다는 생각..



필요한 테이블은 크게 유저 / 게시판 / 댓글 / 사진관리 이기 때문에 간략하게 구성함. 



2. DB 서버 연결


이번엔 Naver 클라우드 서버에 있는 DB를 활용.

근데 생각해보니 사진을 저장할 스토리지도 연결해야 하는데 그건 어떻게 해야하지?...



기존에 개념적으로 너무 가볍게 읽고 간 부분이라서 좀 깊숙히 배우려 노력하였지만.. 역시나 어려워서

코드로 배우는 스프링 책을 참고 하였다.(스프링을 배우기에 가장 좋은 책이라 생각함)

0. 파일을 올리는 것은 살짝 다르다.

일단 파일 자체는 DB에 저장이 되지 않는다. 따로 Storage를 통해 저장을 한다.

(물론 파일명을 저장할 DB는 필요로 하다)

방금 위에서 언급 했지만 제일 중요한 것은 바로 파일 명이다.

1. 파일을 업로드 한다는 것은 결국 파일의 경로를 지정해주는 일이다.

즉 쉽게 생각하면 경로 하나에(폴더 하나에) 모든 파일을 다 때려 넣을 수 있으나

이럴 경우 추후 관리하기 힘들므로


- 파일 마다 고유의 랜덤값 부여를 통한 중복 방지

- 파일 경로를 년, 월, 일로 구분하여 추후 관리를 편리하게

- 이미지 파일 등의 특정 파일들은 다른 기능들을 추가(염두) 


등으로 구분 할 수 있다.


즉 파일 업로드를 위해선 파일명과 경로(폴더)를 어떻게 생성해야할지 고민하고 이를 코드로 표현 하는 것이다.


2. 파일명을 고민 하기 전에 폴더부터 생성하자.

즉 abc라는 큰 폴더 아래 2018/06/04 이런식으로(2018, 06, 04 이렇게 3개의 폴더를 생성) 생성하는 것이 목표이다.

따라서 위에 폴더를 생성하고 이를 String으로 표현하여 사용하면 된다.

다만 이를 좀더 편하게 하기 위해 xml 상에 bean으로 생성하고 해당 bean을 resource로 넣는 방법도 있다.




3. 이제는 위에 경로 안에다가 내가 만든 경로 규칙을 통해서 넣어주면 된다.


위에처럼 ajax로 신호를 받으면 ResponseEntity를 통해서 대답을 한다.

경로를 생성하는 부분은 fileUtils 라는 메서드를 통해서 진행한다

이 친구는 uploadPath(파일 폴더 경로 중 제일위), file의 원본이름, file 데이타를 매개인자로 받는다.


4. 파일의 폴더 생성하는 메서드는 아래와 같다.


3개의 경로를 받으면


4.1. UUID를 통해 랜덤값을 생성하고 이를 file이름 앞에 붙인다

극단적 예시는 다음과 같다... : afasfjlkas_hoya.jpg  이런식으로 uuid + "_" + filename이 생성된다.


4.2. path를 생성 해주는데 이를위해서 calcPath라는 메서드를 사용한다

바로 아래에 저장해도 되지만 관리의 용이성을 위해 년, 월, 일 등으로 구분하려 한다.



이 친구이다. 뭔가 엄청 복잡해 보이는데 결국은 년, 월, 일만 뽑고 이를 경로로 표현 하는 친구이다...

중간에 DecimalFormat("00")은 단지 월의 표현을 두글자 06, 12, 등으로 표현하기 위해 사용한다.


makeDir이란 함수는... 경로를 만들어준다. 즉 calcPath는 년,월,일의 경로만 생성하고 이를 makeDir에 넘겨서 폴더를 만든다.



paths는 총 3개의 인자를 갖고 있는 배열이다. 그리고 제일 마지막 인자(총 3개의 인자니까 인덱스로는 2번)가 존재하면 경로 생성안한다(바로 return)

위의 경우가 아닐 경우 폴더를 만든다.



5. 다시 돌아오자.


이제 파일과 경로가 다 생성되어 저장까지 되었다. 중간부분에 formatName 부분은 이미지 파일을 구분해주기 위해 추가한 메서드 이다.

이는 나중에 다시 설명하겠다..


다시금 프로젝트 진행을 위해

지난번 확인했던 junit을 사용하려 햇으나

뜬금포로 저러한 에러가 뜸.

디펜전시나 빌드패스 에러 인듯 하나

정확한 이유를 찾지 못했음.


http://vicki.tistory.com/719

이분이 쓰신대로 해보려 했으나 나는 제대로 다 있었음...

결국 뭐든지 최고는 다시 시작하는것이라는 생각으로
기존에 있던 git을 새로 클론하여 시작하니 잘 된다...

흠..

지난 프로젝트때 아예 신경을 안썻던 게시판을 이번에 간단히 만들기로 하였다.

다행히 지난번처럼 게시판의 종류는 많지 않게 간단히 DB설계를 하였고 최소한의 기능만을 가진

게시판을 구축하기로..


1) 게시판 내용을 가져오는 것 : 간단하다. List<E>로 받아서 그대로 가져오면 된다.

ModelAndView로 주면 되니 어려운 것은 없다.


2) 게시판 모양 구축 : 페이징 처리.. 이게 어렵다.

쉽지않다. 아직도 진행중이다.

책을 보고 따라 구현해봤으나 확 와닿지 않아서 내 스타일대로 바꾸었다.


2-1) 일단 페이징을 위한 클래스를 따로 생성했다. 책에선 이를 2가지로 구분했지만 나는 일단

유지보수를 생각안하고(...) 하나로 합쳤다.

페이징 클래스를 생성하는 이유는 다음과 같다.


2-2) 기능을 구현하기 위해 일단 흐름을 생각해본다.

게시판을 클릭하면 게시판.jsp로 넘어간다.


이때 db에 저장된 게시글들을 읽어올 것이다 ---- 1번
게시글이 많을 경우 아래에 페이지가 넘버링 되고 클릭하면 다른 게시들을이 나온다 -----2번

prev, next 등의 버튼을 통해서도 이동할 수 있다 -----3번

위의 3가지가 핵심이라 할 수 있다.

1번에서 중요한 점은 from - to를 명시해주는 것, 즉 내가 20개씩 글을 보여주겠다 생각 한다면

0-19, 20-40, 41-59 이런 식으로 짤라줘야 한다는 점.
이를 위해 시작글번호, 끝 글번호가 필요로 하고 이를 클래스에 멤버변수로 만들었다.

(끝글번호 = 시작글번호 + 20)
# 위 두개의 멤버변수는 쿼리문에서 limit 시작 글번호, 끝 글번호 로 불리게 된다.

2번. 게시글이 많다 라는 것은 총 게시물 대비 내가 보여줄 갯수를 비교하여 페이지를 만들어야 한다는 것이다.
만약 총 게시글이 110개이고 20개씩 보여준다면 총 6페이지가 나와야 한다.
그럼 아래 페이지 넘버링은 1부터 6이 될 것이다.

이를 위해서 총 게시글을 db에서 읽어오는 쿼리문과 메서드가 필요로 하고, 이를 저장 할 수 있는 멤버변수 또한 필요하다.
# 하지만 db에 12만게의 게시글을 만들어 놨더니 게시글 불러오는데에 너무 오래걸린다. 이거는 좀더 생각해 봐야할듯

3번 prev는 이전, next는 다음인데 여기서 next를 +1 씩 시킬지, +@로 할지 아직은 안정한 상태

다만 prev의 경우는 현재 페이지가 1일때는 보여서는 안되며 next역시 더이상 다음에 불러올 페이지가 없을 경우에

보여서는 안된다.
그렇기 때문에 prev, next 는 boolean 멤버변수로 설정하여 true, false 값으로 조절 하게끔 해야한다.

1,2,3번을 종합해보면 페이징 클래스에는
시작글번호, 끝글번호, 총글갯수, prev, next 등의 멤버변수들과 이를 설정해주는 다른 메서드들이 추가로 필요함을 알 수 있다.


기본적인 매퍼이다. 게시글에 대한 정보 + 회원에 대한 정보를 조인해서 가져와야 나중에 수정 등을 할 수 있다. 아마 필요하다면 계속 바뀔 매퍼

페이지를 관리하는 클래스 이다. 책에선 이것을 두개로 나누었지만 그게 오히려 복잡해서 하나로 합쳤다. 기본적으로 현재 페이지, 끝페이지, 시작 게시글, 끝 게시글, 이런식으로 구분 하였다. 어제에 비해서 바뀐 것은 next, prev에 값을 갖는 멤버변수를 만들었다는 점이다. 웹상에서 숫자 계산을 해야하는데(예를 들어 현재 페이지가 15이면 next를 누르면 25페이지로 넘어가게끔) 이를 어떻게 해야할지 몰라서 일단 서버에서 다 해결하는 것으로 하였다.

prev, next의 조건을 계산 하기 위한 값. next 조건의 경우 아직 완벽하지 않다.

ModelAndVIew로 건네준다. 글 목록과 페이지관리 클래스를 같이 넘겨준다. 글 목록은 항상 20개씩 나오는데 문제는 불러오는 속도가 너무 느리다는 점. 아무래도 총 게시글 갯수를 확인하는 것 때문에 그런것 같아서 if문으로 한번만 할 수 있게 조건을 주었다. 그래도 느리다. 한가지 더 신경쓴 점은 RestfulAPI를 적용하기 위해 URL을 단순히 하였고 bbs는 기본적으로 ?page=num 식으로 이루어지므로 value, defaule값 등을 주었다.







RestFul,

Rest...


많은 기업에서 RestFul api 경험을 묻곤 한다.


처음에는 새로운 언어인가 싶었다. 자주 사용되는 언어인가 싶었는데 그것은 다행히 아니였다.

RestFul 이란 아키텍쳐 스타일, 즉 설계시 사용하는 스타일 이라고 한다.

Resource에 집중하여, 즉 정보에 집중한다고 하는데...


암만 찾아봐도 다들 설명이나 예제가 비슷하다.

그래서 내가 이해한 것은 


1. URL을 리소스 위주로 명시하자.

회원 가입 :  /register 이 아닌 /users(post로)

2. json을 사용하자


이정도...

뭔가 알아내면 더 추가해야지.. 지금은 머리속이 너무 복잡하다.

# 5.24 추가
1.내가 내린 Restful API의 결론은 자원을 직관적으로 표기하는 것
2.그리고 JSON을 활용하는 것(되도록)

1을 위해서 URL을 최초에 설계할 필요가 있어 보임.
2의 경우 Spring에서 ModelAndVIew가 해결해준다고 하니 일단 이러한 방식으로 접근해봐야겠다.

# 5.25 추가
스프링 4.0부터는 Rest방식을 지원하기 위해 @Restcontroller를 지원함
기존에 json 통신을 위해 @responsebody 등을 해줘야 했던 것에서 탈피하여 컨트롤러 하나를 통채로 사용할 수 있는듯.



회원 가입 기능 구현


0. 로직

- 클라이언트로부터 input 값을 받음 

-  dispathcer가 컨트롤러로 분산

- annotation에 의해 vo에 매핑

- 받은 값(vo)를 dao로 전달

- dao를 통해 insert


큰 순서는 이러하고 이를 위해서 사전에 몇가지 해줘야 하는 작업이 있다.


첫번째론 dataSource와 mybatis-config.xml을 연결해줘야한다.

mysql과 연결만 되어있을뿐 이를 위한 쿼리, 타입 등의 설정을 해줘야 한다.

그래서 mybatis-config.xml(타입과 매퍼 연결), mapper.xml(쿼리문 작성) 을 해줘야 한다.


1. 클라이언트 input


form 태그를 이용하여 action에 주소값만 설정해주면 된다.

단 input 태그로 전달되는 값과 vo를 매핑하기 위해선 input 태그의 name 속성의 이름을 vo 클래스와 동일하게 맞춰야 한다.

vo에 String apple 이라는 변수가 있고 이를 연결해준다면 input 에도 역시 name = "apple" 라고 해줘야 알아서 매핑이 된다.


2. dao

이번 프로젝트에선 biz를 생략했다. 지난번 프로젝트에서도 거치는 관문 용으로만 사용하여서.

아마 필요로 하면 통합형으로 하나 만들 것 같다.

dao로 바로 보내면 sqlsessionfactory로 연결을 하고 mapper를 이용하여 insert 하면 된다.

Java, Spring 웹 서비스 개인 프로젝트.

목표 : 기본 기능 구현

- 회원가입

- 로그인

- 파일 업로드

- 게시판


지난번에 못했던 것 위주로 진행.

제일 처음에 환경 설정 하는 것부터 시작.


0. 개발환경

- jdk 1.8

- Eclipse oxygen

- tomcat 8.5

- Mysql 5.7버젼


1) java 설치의 경우

jdk가 jre를 포함 하고 있기 때문에 jdk만 설치하였음.


2) Eclipse는 안전하게 oxygen..


3) tomcat은 8.5버젼, 이유는 지난번에도 이것을 썻기 때문.

tomcat은 인스톨러로 설치 한 후에 윈도우에서 서비스를 꺼놔야 편함

(그렇지 않을 경우 매번 끄고 프로젝트를 진행해야해서 에러 마주칠 확률이 큼)


4) mysql은 8.0으로 설치해서 진행했는데

스프링에서 드라이버 연결시 에러가 발생.. cashing sh 관련 에러인데, 권한이랑 비밀번호 관련 문제 인듯 함.

이 문제를 해결하니 cashing size 에러가 발생해서 그냥 다운 그레이드로 하기로.


5) 지난번에는 다이나믹 웹프로젝트로 생성한 후 maven으로 변경을 시켰음. 

이번에는 spring legacy project를 통해서 바로 maven template를 적용함

- 이렇게 하고 나니 깔금하게 잘 정리되서 나와서 좋았음.

- 제일 중요한건 log4j를 붙여준다라는 점.

(이거 설정할때 너무 힘들었어서.. 아직도 잘 모르겠음)


5-1) 물론 설정파일들이 서로 연동된다는 사실을 모르면 안되므로 기존에 했던 방식이 필요로 하긴 했지만

이번에 할때는 새로운 방식으로 하기로.


6) mysql 연동시


6-1) connector를 다운 받아서 붙여줘야함.

이는 불필요할 수도 있음.

이를 하는 이유는 이클립스 상에서 sql을 조작하기 위함임.

maven은 라이브러리를 porm.xml로 붙이기 때문에 crud가 가능함.

다만 우리가 DB를 설계한 후에 바로 생성하기 위해서, 또는 조작하기 위해서 매번 cmd를 킬 순 없으니 connector를 사용


6-2) connector를 build path로 설정을 해놔야 database 연결시 드라이버가 사용 가능 하다.


'자바' 카테고리의 다른 글

3회차) 첨부파일 업로드 기능  (0) 2018.06.04
번외) No junits test found  (0) 2018.05.31
3회차) 게시판을 만들자  (0) 2018.05.24
2.5회차, RestFul api에 대한 고민  (0) 2018.05.21
2회차 회원 가입 기능 구현  (0) 2018.05.21

+ Recent posts