개발공부를 하다 포트폴리오와 모의면접을 진행하며 어쩌면 나.. PM이 되고 싶은 걸 수도? 싶었고 과감하게 기획자용 포폴을 따로 만들어 PM 취업에 성공하게 되는데.. (인간극장BGM ~)
2월 - 월화수목금토일 밤낮없이 일하고 공부하다 | 난이도 ★★★★★
3월 - 놀다 | 난이도 ☆☆☆☆★ (약간의 심적 부담감)
4월, 5월 - 설렁설렁 프로그래밍 공부하다 | 난이도 ☆☆☆★★
6월 - 경각심을 가지고 공부하다 | 난이도 ☆☆★★★
7월 - 심각성을 느끼며 공부하다 | 난이도 ☆★★★★
8월, 9월 - 일상이 공부에 잡아먹히다 공부 외엔 한 것 X | 난이도 ★★★★★
10월 - 백수가 되어 취준, 결국 취업하다 | 난이도 ★★★★★ (일생일대의 가장 끔찍한 시간)
개발자가 되겠다고 떠났던 나의 인생은 그동안 이렇게 흘러갔다.
공부를 하며 잘 되는 날도 있고 잘 되지 않는 날도 있었고.. 심적인 부담감과 압박감이 정말 큰 날도 있었다.
그래도 다 지나고 보니 뿌듯했던 시간들. '지나고 보니' 를 강조하고 싶다. 기억은 왜 미화되는지 참
다행인건 사람 때문에 힘든 게 없었다는 것! 이번 직장에서도 날 채용하시는 분이 (팀장님이신것 같았다) '사람 때문에 힘든 일은 없게 해줄 자신 있어요!' 라고 하신 말이 참 든든했는데 제발 그게 사실이 되길! 그리고 내가 빌런이 되지 않길!
나도 성실함은 자신있다! 아자!
연봉이 매우 낮고 야근이 잦다고 하지만 이 회사를 선택한 이유는 확실하다.
내가 해 본 교육 분야 콘텐츠 제작 + 잡플래닛 평점이 좋음 + 날 가르쳐주겠다는 마인드가 보임 + 선임의 자신감.
내가 이 일을 하고 싶어 갔는데 이 분야의 자칭 최고라는 사람이 잘 가르쳐 주겠다 하면 나는 땡큐다.
신입 인큐베이팅이 어려워 있는 사람도 다 짜르고 경력있는 말만 신입을 뽑기 바쁜데 여기서는 사람을 가르친다는 게 정말 좋아보였다. 딴 곳은 회사가 학원도 아니고 가르치고 말고 하는 게 뭐가 있냐 하는데 여긴 마인드 자체가 달랐다.
실제 들어가서 보면 어떨지 모르겠지만 나도 들어가서 꼭 잘 성장하고 싶다.
취준을 하며 이력서 + 자소서도 쓰고 면접도 보며 느낀 게 많아 정리해보려 한다.
1. 성실하게 살아온 인생은 다 알아준다.
-> 삶에 공백이 있고 그 공백을 채울만한 결과물이 없더라도 이 전 직장의 근태기간이나 공부하며 만든 포트폴리오의 기여도, 학점 등을 보면 그 사람의 성실도를 알 수 있다.
2. 학력이 좋지 않아서 주눅들어 있었지만 그건 중요하다고 생각하는 사람에게나 중요한 거였다.
-> 말 그대로. 대기업 말고는 학력을 잘 안본다. 그리고 우리나라에 대기업은 생각보다 별로 없다. 초대졸/대졸 연봉 차이는 있는 곳도 있고 없는 곳도 있다.
3. 난 말을 잘 한다.
4. 글도 좀 쓴다.
-> 꾸며내는 말을 잘 하는게 아니라 수치적 성과를 보여주지 못하는 일을 했다? 하면 수치적으로 만족도 조사한거 통계 내서 결과 알려주고, 뭐라도 이색적으로 전 직장에서 한 걸 말하고 이런 식으로 약간의 응용력이 있었다. 전 직장과 여기의 업무 분야가 달라도 전 직장에서 했던 일을 이 직장에서는 어떻게 적용할 수 있는지 어필을 했고 그게 통한 것 같다.
5. 절실하면 말이 길어지는데 이럴 때일수록 경청이 중요하다.
-> 날 의심하는 사람에게는 날 어필하고 싶어 말이 길어지곤 했는데 그럴때마다 그 사람이 뭘 말하고자, 뭘 듣고 싶어하는지 알기 위해 경청했다. 말이 길어지는 건 상대가 지루해지는 일이고 면접은 만담이 아니기때문에 상대가 듣고 싶어하는 이야기를 들려주면 된다. 답은 없어도 상대가 원하는 인재상과 내가 일치해야 뽑히기 때문에 상대가 원하는 답을 내 상황에 적용해서 해줘야하고, 그걸 알기 위해 경청하며 의중을 파악하는 것이 중요하다.
6. 면접에서 1분 자기소개는 기세다!
-> 따뜻하게 반기는 분위기에서도, 대기자가 많고 차가운 분위기에서도 면접을 봤었는데 이 분위기를 반전시키거나 고조시킬 수 있는게 1분 자기소개다. 면접의 분위기에서 압도당하지 않으려면 1분 자기소개는 기세로 밀고 붙여야한다.
7. 기획이 적성이다!
8. 면접은 많이 보는게 좋다.
-> 면접관은 보통 나보다 그 분야의 경력이 많고 나이도 많고, 사람도 많이 만나봤다. 그렇기 때문에 나를 파악하는 속도가 빠르고 판단도 빠르게 내린다. 성격이나 분위기 등을 파악하며 나에 대해 말해주신 것들은 대부분 개발보다는 기획이 적성이라는 것.
글 쓰는 것이나 말하는 것도 기획쪽에서 더 요구하고 필요한 능력이라고 이야기해주셨다.
다양한 면접 분위기에 적응하고, 나를 잘 알기 위해서라도 면접을 많이 보는 것이 취준생에게는 좋은 것 같다. 물론 심적으로+물리적으로 힘들긴 하지만.. 내가 일하고 싶은 곳에서 일을 하려면 그 정도는 감수 할 수 밖에 없는 현실이다 ㅠ
8개월 가량 되는 취업준비기간이 쉽진 않았지만 아주 못할 것도 없는 것 같다.
국비지원학원을 다니게 되면 국취제 지원도 받고 훈련참여수당으로 학원에도 돈도 나와서 생계에도 크게 지장이 가지 않았다.
물론 난 모아둔 돈이 있었지만, 지원금 덕에 적금을 깨지 않아도 됐고 좀 더 맘 편히 공부할 책도 사고 밥도 먹고 교통비도 충당하고 그랬다. 매일 늦게까지 공부하다보니 밥값이 정말 많이 들었는데 지원금 덕에 라면만 먹지 않았다. 지원이 끝나고 백수가 되어서는 계속 라면만 먹고 있지만.. 돈을 모아뒀어도 감당이 안되는 물가인만큼 이 지원금 정책은 정말 도움이 된다.
국비학원은 너무 강의의 질을 생각하기 보단 여기서 배우는 것이 정말 개발자가 되기 위한 기초라는 것을 알고 그 기초를 다진다는 생각으로 임해야 하는 것 같다. 사전에 자바랑 자바스크립트 책 사서 프로그래밍을 해보고, 수업들으며 또 기초 강의도 듣고 그래야 실력도 늘고 이해도 된다. 나도 자바 배울 때 생활코딩이랑 남궁민 자바의 정석 강의 + 책을 보며 공부했다.
자바스크립트는 전에 스파르타 코딩클럽 강의를 봤을 때 쉽게 가르쳐 준 기억이 있어 다시 봤다. 그리고 노마드 코더 바닐라 자바 스크립트도 봤었다. 나중으로 갈 수록 어려워지는데 제이쿼리랑 같이 복습 할 수 있어서 어려워도 그냥 후루룩 참고 보다보니 나중에 이해가 됐다. 프로그래밍 언어는 좀 그런 면이 있는 것 같다. 처음에는 어렵고 계속 이해 못할 것 같아도 손이나 머리가 외워서인지 어떻게든 이해가 되는 날이 온다!(?) 진짜임.. 나도 아직 잘 모르지만.. 진짜인듯..?
하루가 모자르게 공부를 하다 중간에 잠깐 10시까지 남는 동료들과 코딩테스트 문제도 풀어보고, 진짜 잘 하는 사람은 이걸 지능테스트라고 하던데..(....) 암튼 지능이 좀 모자란 난 그냥 강의 더 보고 배운 거 정리하고 그랬다..
그 결과 단위테스트라고 의미가 크게 있진 않지만 내 현행수준을 체크하는 시험이 있었는데 거기 성적이 나쁘지 않게 꾸준히 잘 나왔다. 초반엔 좀 안좋았다 올라서 '성적향상상'을 받기도 했다. 나름 문상도 주고 격려받는 느낌이어서 좋았다.
포폴할때는 진짜 좀 힘들었는데 GPT 활용을 엄청 해댄 덕분에 무사히 끝났다. 이때부터 기획 쪽이 어쩌면 더 적성이다 생각은 계속 한 것 같다. 기획도 재미있었고 나중에 발표 준비도 재미있었고.. PPT 만드는 것도 재미있었음
프로그래밍 물론 좋지만.. 이거 하는것도 이렇게 압박감이 드는데 오래 할 수 있을까 이 생각이 들었다.
오늘 오전 다시 파일들을 찬찬히 살펴보며 코드를 수정했는데 pom.xml 에 세팅 오류가 있었다.'
오류 메세지 )
Context initialized 이벤트를 [org.springframework.web.context.ContextLoaderListener] 클래스의 인스턴스인 리스너에 전송하는 동안 예외 발생 org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'boardDAOMybatis':
Unsatisfied dependency expressed through field 'mybatis'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.mybatis.spring.SqlSessionTemplate#0' defined in classpathresource [applicationContext.xml]:
Cannotresolvereferencetobean 'sqlSession' whilesettingconstructorargument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sqlSession' defined in classpathresource [applicationContext.xml]:
Initializationofbeanfailed; nested exception is java.lang.reflect.MalformedParameterizedTypeException
ibatis-core는 그리고 어디서 왜 가지고 온 것일까..? 과거의 내가 그랬다니 믿을 수가 없다.
롬북의 버전이 다른 것, ibatis-core 부분은 큰 문제는 아니지만 aspectjweaver 태그가 앞쪽에 들어가야 인식이 되는 건지 강사님이 aspectjweaver 태그를 넣은 부분으로 코드를 옮기자 실행이 됐다. 이클립스의 오류인지 실행 순서의 오류인지 알 수가 없다.
GPT에게 이 문제에 대해 물어봤다.
질문 : 스프링으로 프로젝트를 할때 pom.xml에서 aspectjweaver태그 위치가 실행 오류와 연관이 있어?
답변 :
`pom.xml` 파일에서 `aspectjweaver` 태그의 위치는 **실행 오류와 직접적인 연관이 없습니다**. 하지만, `pom.xml`에서의 태그 위치는 Maven 빌드 순서나 종속성 관리에 영향을 줄 수 있습니다. 실행 오류는 주로 다음과 같은 원인들로 발생할 수 있습니다:
### **1. 종속성 충돌** - Maven에서는 여러 라이브러리를 가져올 때, 동일한 라이브러리의 다른 버전이 서로 충돌할 수 있습니다. 이런 경우 `aspectjweaver`가 포함된 위치와 상관없이, 해당 라이브러리의 버전 충돌로 인해 실행 오류가 발생할 수 있습니다.
### **2. 종속성 범위(scope)** - `aspectjweaver` 라이브러리의 `scope`가 잘못 설정된 경우, 예를 들어 `test` 범위로 설정되어 있는데 실제 애플리케이션 런타임에서 필요할 경우, 런타임 오류가 발생할 수 있습니다.
### **3. 종속성 순서** - Maven의 종속성은 일반적으로 선언된 순서대로 적용됩니다. 이 순서가 의존성 해결에 영향을 미칠 수 있습니다. `aspectjweaver`가 특정 다른 라이브러리에 의존하고 있고, 그 의존성이 올바르게 해결되지 않을 경우 문제를 일으킬 수 있습니다.
### **4. 종속성 버전** - `aspectjweaver` 라이브러리의 버전이 프로젝트에서 사용 중인 다른 라이브러리와 호환되지 않을 경우, 실행 중 오류가 발생할 수 있습니다.
### **5. Maven 빌드 플러그인** - `aspectjweaver`를 사용하는 동안, 필요한 Maven 빌드 플러그인(예: AspectJ Maven 플러그인)이 올바르게 설정되지 않은 경우에도 실행 오류가 발생할 수 있습니다.
### **올바른 위치** 일반적으로 `aspectjweaver`는 `dependencies` 섹션 내에 위치하며, 다른 종속성과 함께 나열됩니다. 예를 들어:
### **오류를 방지하는 방법** 1. **정확한 버전 관리**: 모든 종속성의 버전을 일관되게 관리합니다. 2. **종속성 충돌 방지**: `mvn dependency:tree` 명령을 통해 의존성 트리를 확인하고, 충돌이 있는지 검사합니다. 3. **종속성 우선순위 확인**: 특정 종속성이 다른 종속성보다 먼저 로드되도록 해야 한다면, `pom.xml`의 선언 순서를 조정할 수 있습니다. 4. **빌드 플러그인 설정**: 필요한 경우 AspectJ Maven 플러그인이나 다른 관련 플러그인을 설정합니다.
### **마무리** `aspectjweaver` 태그의 위치가 실행 오류의 직접적인 원인은 아니지만, Maven 종속성 관리에서의 위치, 버전, 범위(scope), 그리고 다른 라이브러리와의 충돌 여부는 오류 발생과 밀접한 관련이 있습니다. 따라서 이러한 요소들을 종합적으로 고려하여 문제를 해결하는 것이 중요합니다.
휴.. 쉽지 않다.
버전을 강사님과 다 맞췄는데 안됐고, 나 뿐만 아니라 다른 동료들도 같은 오류가 난 것으로 보아
강사님도 저 오류가 나 파일을 수정하셨는데 공유가 되지않았고
저 오류의 가장 유력한 후보는 일단 3번인 것 같다.
강사님이 전에 주신 pom.xml 파일과 비교해도 aspectjweaver 태그의 순서 말고는 다른 부분이 없다!
GPT의 답변이 다 믿을만한 것은 아니지만 방대한 통계로 해준 답변일테니
앞으론 저런 오류가 날 수도 있구나 정도로 알고 코드를 잘 봐야겠다!
삽질이라면 삽질이지만 언제 또 이렇게 pom.xml을 자세히 볼까!? 정말 엄청난 깨달음이다.
이참에 정리하는 aspectjweaver 태그란?
Maven 프로젝트에서 AspectJ 라이브러리를 사용하기 위해 선언하는 종속성을 지정하는 역할.
AspectJ는 AOP (Aspect-Oriented Programming, 관점 지향 프로그래밍)를 구현하기 위한 도구로,
src/main/resources > mapping > board-mapping.xml 에서 쿼리문을 처리
BoardDAO에서는 board-mapping.xml에서 적은 쿼리문을 언제 사용할지 정의해준다고 생각하면 됨.
board-mapping.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="BoardDAO">
<!-- 글 등록 -->
<insert id="insertBoard">
insert into board(seq,title,writer,content)values((select nvl(max(seq),0) + 1 from board), #{title}, #{writer}, #{content})
</insert>
<!-- 글 수정 -->
<update id="updateBoard">
update board set title=#{title}, content=#{content}, where seq=#{seq}
</update>
<!-- 글 삭제 -->
<delete id="deleteBoard">
delete board where seq=#{seq}
</delete>
<!-- 글 상세 조회 -->
<select id="getBoard" resultType="board">
select * from board where seq=#{seq}
</select>
<!-- 글 목록 조회 -->
<select id="getBoardList" resultType="board">
select * from board where title like '%'||#{searchKeyword}||'%'
order by seq desc
</select>
</mapper>
package com.springbook.biz.board;
import com.springbook.biz.board.impl.BoardDAO;
publicclassBoardServiceClient{
publicstaticvoidmain(String[] args){
BoardDAO boardDAO = new BoardDAO();
BoardVO vo = new BoardVO();
vo.setTitle("myBatis 제목");
vo.setWriter("홍길동");
vo.setContent("여름아 부탁해");
boardDAO.insertBoard(vo);
}
}
이렇게 하고 실행했는데 이런 경고 메세지가 생겨서 보니
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by org.apache.ibatis.reflection.Reflector (file:/C:/Users/USER/.m2/repository/org/mybatis/mybatis/3.3.1/mybatis-3.3.1.jar) to method java.lang.Object.finalize()
WARNING: Please consider reporting this to the maintainers of org.apache.ibatis.reflection.Reflector
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
mybatis 버전 문제였다. 3.3.1 버전에서 → 3.5.2 버전으로 업그레이드 한 후 실행했더니 해결
package com.springbook.biz.board;
import java.util.List;
import com.springbook.biz.board.impl.BoardDAO;
publicclassBoardServiceClient{
publicstaticvoidmain(String[] args){
BoardDAO boardDAO = new BoardDAO();
BoardVO vo = new BoardVO();
// 글 등록
vo.setTitle("myBatis 제목");
vo.setWriter("홍길동");
vo.setContent("여름아 부탁해");
boardDAO.insertBoard(vo);
vo.setSearchCondition("TITLE");
vo.setSearchKeyword("");
// 상세글 조회//vo.setSeq(19);//BoardVO getboard = boardDAO.getBoard(vo);//System.out.println("상세글 조회 결과: " + getboard);// 글 수정//vo.setTitle("오늘의 날씨");//vo.setContent("맑음");//vo.setSeq(17);//boardDAO.updateBoard(vo);// 글 삭제//vo.setSeq(29);//boardDAO.deleteBoard(vo);// 전체 글 조회
List<BoardVO> boardList = boardDAO.getBoardList(vo);
for (BoardVO board : boardList) {
System.out.println("==>" + board.toString());
}
}
}
조회, 수정, 삭제가 안된다..! 미해결과제
해결 )
추가로 알아 둘 것.
board-mapping.xml
<selectkey keyProperty="seq" resultType="int" order="BEFORE">
select board_seq.nextval as seq from duel
</selectkey>
insert into board(seq,title,writer,content)values(#{seq}, #{title}, #{writer}, #{content})
<!-- 어떤 값을 따로 쓰고 싶을 때 -->
<!-- order="AFTER" insert 처리가 된 후에 값을 가지고 와라 -->
<selectkey keyProperty="writer" resultType="int" order="AFTER">
select writer from board
</selectkey>
</insert>
<insert id ="hobby">
insert into hobby(writer)values(#{writer}) <!-- 다른 테이블의 writer를 끌어올 수 있다 -->
</insert>
시퀀스 객체를 DB에 만들어 두고 시퀀스 객체를 이용할 수 있다.
board-mapping.xml
<!-- 컬럼이름과 VO객체 멤버 이름 비교, 이름이 다르더라도 자동으로 매치가 된다 -->
<resultMap id="boardResult" type="board">
<id property="seq" column="SEQ"/>
<result property="title" column="TITLE"/>
<result property="writer" column="WRITER"/>
<result property="content" column="CONTENT"/>
<result property="regDate" column="REGDATE"/>
<result property="cnt" column="CNT"/>
</resultMap>
쿼리문을 쓸 때 부등호( <, > ) 인식을 못한다. 그럼으로 부등호를 써야할 때는
<!-- 조건절에 비교연산자(<, >)을 식으로 인식하기 위해 -->
<![CDATA[
insert into board(seq,title,writer,content)values((select nvl(max(seq),0) + 1 from board), #{title}, #{writer}, #{content})
]]>
<![CDATA[ ]]> 안에 비교할 대상의 식이 쓰이게 사용해야 한다.
BoardDAO.java
package com.springbook.biz.board.impl;
import java.sql.SQLException;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import com.springbook.biz.board.BoardVO;
import com.springbook.biz.util.SqlSessionFactoryBean;
publicclassBoardDAO{
// 원형// public List seleList(String statement)// public List seleList(String statement, Object parmeter)// // public int insert(String statement, Object parmeter)// public int update(String statement, Object parmeter) throws SQLException// public int delete(String statement, Object parmeter) throws SQLException// SqlSession 타입 mybatis은 xml의 정보를 다 가지고 있다. private SqlSession mybatis;
publicBoardDAO(){
mybatis = SqlSessionFactoryBean.getSqlSessionInstance();
}
// 글 등록publicvoidinsertBoard(BoardVO vo){
mybatis.insert("BoardDAO.insertBoard", vo);
mybatis.commit();
}
// 글 수정publicvoidupdateBoard(BoardVO vo){
mybatis.update("BoardDAO.updateBoard", vo);
mybatis.commit();
}
// 글 삭제publicvoiddeleteBoard(BoardVO vo){
mybatis.delete("BoardDAO.deleteBoard", vo);
mybatis.commit();
}
// 글 상세조회public BoardVO getBoard(BoardVO vo){
return mybatis.selectOne("BoardDAO.getBoard", vo); // 하나만 조회할때 selectOne
}
// 글 목록 조회public List<BoardVO> getBoardList(BoardVO vo){
return mybatis.selectList("BoardDAO.getBoardList", vo); // 여러 개 조회할때 selectList
}
}
Spring에 Mybatis 연결하기
어떤 프로그램을 사용하기 위해서는 접점이 필요하다
Spring에서도 Mybatis을 사용하기 위해서는 SDK → api 인터페이스가 필요.
SDK 을 통해 Mybatis에 접근한다.
자바에서 오라클DB를 쓰는 것과 똑같은 원리. - JDBC가 SDK
다이나믹 SQL = 동적 쿼리문
조건절 분기
BoardWep > board-mapping.xml
<!-- 글 목록 조회 -->
<select id="getBoardList" resultType="board" resultMap="boardResult">
select * from board where title like '%'||#{searchKeyword}||'%'
order by seq desc
</select>
위와 같은 구문 고정이 title인데 동적 처리를 하면
BoardWep > board-mapping.xml
<!-- 글 목록 조회 -->
<select id="getBoardList" resultType="board" resultMap="boardResult">
select * from board
where 1=1
<if test="searchCondition == 'TITLE'">
and title like '%'||#{searchKeyword}||'%'
</if>
<if test="searchCondition == 'CONTENT'">
and content like '%'||#{searchKeyword}||'%'
</if>
order by seq desc
</select>
이렇게 바꿀 수 있다.
where 절의 중복을 막기 위해 where 1=1 를 쓰고 where 절이 들어가는 곳마다 and 를 쓴다.
BoardWep > BoardDAOMybatis
package com.springbook.biz.board.impl;
import java.util.List;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import com.springbook.biz.board.BoardVO;
@RepositorypublicclassBoardDAOMybatis{
// SqlSessionTemplate이 mybatis 기능을 쓰는 라이브러리// SqlSessionTemplate 객체를 사용// 자동의존주입@Autowiredprivate SqlSessionTemplate mybatis;
publicvoidinsertBoard(BoardVO vo){
System.out.println("==> Mybatis로 insertBoard() 기능 처리");
mybatis.insert("BoardDAO.insertBoard", vo);
}
publicvoidupdateBoard(BoardVO vo){
System.out.println("==> Mybatis로 updateBoard() 기능 처리");
mybatis.update("BoardDAO.updateBoard", vo);
}
publicvoiddeleteBoard(BoardVO vo){
System.out.println("==> Mybatis로 deleteBoard() 기능 처리");
mybatis.delete("BoardDAO.deleteBoard", vo);
}
public BoardVO getBoard(BoardVO vo){
System.out.println("==> Mybatis로 getBoard() 기능 처리");
return mybatis.selectOne("BoardDAO.getBoard", vo);
}
public List<BoardVO> getBoardList(BoardVO vo){
System.out.println("==> Mybatis로 getBoardList() 기능 처리");
return mybatis.selectList("BoardDAO.getBoardList", vo);
}
}
applicationContext
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
<!-- 패키지를 스캔해준다. 다 해주는 것은 아니고 표시되어진 클래스만 스캔해준다 -> 이것이 어노테이션 -->
<!-- <context:component-scan base-package="polymorphism"></context:component-scan> -->
<context:component-scan base-package="com.springbook.biz"></context:component-scan>
<!-- database.properties 의 파일을 읽어올 수 있게 해준다 -->
<context:property-placeholder location="classpath:config/database.properties"/>
<bean id="dataSource"class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!--JdbcTemplate 클래스를 통해 DB에 접근 (crud) -->
<bean id="jdbcTemplate"class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="txManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="get*" read-only="true"/> <!-- get메소드 제외하고 트랜잭션 처리를 해줘라 -->
<tx:method name="*" />
</tx:attributes>
</tx:advice>
<bean id="sqlsession"class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpth:sql-map-config.xml"/>
</bean>
<bean class="org.mybatis.spring.SqlSessionTemplate">
<!-- 생성자 의존 주입 -->
<constructor-arg ref="sqlSession"></constructor-arg>
</bean>
<aop:config>
<aop:pointcut expression="execution(* com.springbook.biz..*(..))" id="txPointcut"/> <!-- biz 하위의 모든 메소드를 수정하겠다 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut" />
</aop:config>
</beans>
<!-- HttpMessageConverter를 구현한 모든 변환기가 생성된다 --><!-- 자바객체를 JSON응답 보디로 변환할 때는 MappingJackson2HttpMessageConverter를 사용한다 --><!-- 스프링 컨테이너가 어노테이션 처리를 해준다, 우리가 직접 MappingJackson2HttpMessageConverter을 사용하는 것이 아님.--><mvc:annotation-driven></mvc:annotation-driven>
Boardcontroller.java
// 글 목록 JSON으로 변환@RequestMapping(value = "/dataTransform.do")// @ResponseBody 어노테이션을 사용하여 자바 객체를 HTTP 응답 본문의 객체로 변환하며 클라이언트로 전송한다@ResponseBody// 자바형식을 제이슨형식으로 바꿔달라고 요청public List<BoardVO> dataTransform(BoardVO vo){
vo.setSearchCondition("TITLE");
vo.setSearchKeyword("");
List<BoardVO> boardList = boardService.getBoardList(vo);
return boardList;
}
package com.springbook.biz.board.impl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import com.springbook.biz.board.BoardVO;
// 비즈니스 계층에서 사용할 수 있도록 빈에 등록
@Repository
public classBoardDAOSpring{
@Autowired
private JdbcTemplate jdbcTemplate;
// 상수형태로 만들어 두었다.
private final String BOARD_INSERT = " insert into board(seq, title, writer, content) values "
+ " ((select nvl(max(seq),0) + 1 from board), ?, ?, ?)";
private final String BOARD_UPDATE = "update board set title=?, content=? where seq=?";
private final String BOARD_DELETE = "delete board where seq=?";
private final String BOARD_GET = "select * from board where seq=?";
private final String BOARD_LIST = "select * from board order by seq desc";
// 검색기능
private final String BOARD_LIST_T = "select * from board where title like '%'||?||'%' order by seq desc";
private final String BOARD_LIST_C = "select * from board where content like '%'||?||'%' order by seq desc";
// ---------- update() 구문 사용 ----------// 글 등록
public voidinsertBoard(BoardVO vo) {
System.out.println("===> springJDBC로 insertBoard() 기능 처리");
// insert를 springJDBC에서는 update가 처리, try catch문 사용 안해도 됨.// 기존 jdbc 에서 사용한 '?' 대신 순서대로 객체를 가지고 와 적어준다.// jdbcTemplate.update(BOARD_INSERT, vo.getTitle(), vo.getWriter(), vo.getContent());
jdbcTemplate.update(BOARD_INSERT, vo.getTitle(), vo.getWriter(), vo.getContent());
}
// 글 수정
public voidupdateBoard(BoardVO vo) {
System.out.println("===> springJDBC로 updateBoard() 기능 처리");
jdbcTemplate.update(BOARD_UPDATE, vo.getTitle(), vo.getContent(), vo.getSeq());
}
// 글 삭제
public voiddeleteBoard(BoardVO vo) {
System.out.println("===> springJDBC로 deleteBoard() 기능 처리");
jdbcTemplate.update(BOARD_DELETE, vo.getSeq());
}
// ---------- queryForObject(), query() 구문 사용 ----------// 글 상세 조회
public BoardVO getBoard(BoardVO vo) {
System.out.println("===> springJDBC로 getBoard() 상세 보기 처리");
// 배열로 값을 받기 때문에 배열에 넣어준다Object[] args= {vo.getSeq()};
// return jdbcTemplate.queryForObject(sql, args, rowMapper);return jdbcTemplate.queryForObject(BOARD_GET, args, new BoardRowMapper());
}
// 글 목록 조회
public List<BoardVO> getBoardList(BoardVO vo) {
System.out.println("==> springJDBC로 getBoardList() 기능 처리");
Object[] args = {vo.getSearchKeyword()};
if (vo.getSearchCondition().equals("TITLE")) {
return jdbcTemplate.query(BOARD_LIST_T, args, new BoardRowMapper());
} elseif (vo.getSearchCondition().equals("CONTENT")) {
return jdbcTemplate.query(BOARD_LIST_C, args, new BoardRowMapper());
}
returnnull;
}
}
package com.springbook.biz.board;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
@MapperpublicinterfaceBoardMapper{
@Select("SELECT * FROM board ORDER BY seq DESC LIMIT #{pageStart}, #{pageSize}")List<BoardVO> pageList(@Param("pageStart")int pageStart, @Param("pageSize")int pageSize);
@Select("SELECT COUNT(*) FROM board")intboardCount();
// 나머지 메서드들은 기존과 동일
}
정작 BoardDAOSpring은 수정하지 않았는데 접근 자체가 잘못된 것 같다.
다시 해보기 ㅠㅠ
MyBatis
쿼리문을 별도 관리, 분리한다
파일 만들기
여기서 next 를 누르고 조금 기다리면
이런 화면이 나온다
이 파란 부분 선택,
이렇게 설정해 파일을 만든다.
그 후 새로 만든 파일 new → 소스 폴더 누르고 stc/main/java 파일 만들기 → ojdbc6 (맥은 11) 연결하고 톰캣 서버 연결도 해주기.
maven 에서 mybatis, ibatis-core 라이브러리 두 개를 pom.xml에 추가한다.
MyBatis 에서는 BoardDAO에서 쓴 쿼리문을 그대로 사용,
기존 BoardDAO에서 ? 처리된 것을 기존에는 VO를 받아 처리했다면 여기서는 자동으로 VO를 받아와 #{}로 표기한다. 여기서는 VO.getTitle이런 식으로 쓰지 않음.
#{title} 이렇게 표기함으로서 의미를 함축
board-mapping.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="BoardDAO">
<!-- 글 등록 -->
<insert id="insertBoard">
insert into board(seq,title,writer,content)values((select nvl(max(seq),0) + 1 from board), #{title}, #{writer}, #{content})
</insert>
<!-- 글 수정 -->
<update id="updateBoard">
update board set title=#{title}, content=#{content}, where seq=#{seq}
</update>
<!-- 글 삭제 -->
<delete id="deleteBoard">
delete board where seq=#{seq}
</delete>
<!-- 글 상세 조회 -->
<select id="getBoard" resultType="board">
select * from board where seq=#{seq}
</select>
<!-- 글 목록 조회 -->
<select id="getBoardList" resultType="board">
select * from board where title like '%'||#{searchKeyword}||'%'
order by seq desc
</select>
</mapper>
태그 속성 자체에 기능의 이름이 붙으며 쿼리문 사용이 보다 직관적이다.
여러 개의 insert 구문을 만들 수 있는데 이를 구분하는 것이 id값
update, delete 등 다른 것도 마찬가지. id값으로 구분하고, <mapper namespace="BoardDAO"> 의 namespace를통해 접근한다.
위 코드를 통해 presentation-layer.xml 에 컨트롤러, 핸들러, 뷰리저버 정보를 저장한다고 정보를 등록한다.
프론트 컨트롤러 DAO 에 직접 접근이 아니라
비즈니스 계층을 통해 액션 컨트롤러로 접근하는 방식으로 수정
스프링컨테이너 안에 보더 서비스, 보더 컨테이너
보더 서비스를 보더 컨테이너 안에 주입.
프론트 컨트롤러의 요청에 의해서 보더컨테이너는 보더 서비스를 오토와이어
BoardController.java
package com.springbook.view.board;
import java.util.HashMap;
import java.util.Map;
import org.aspectj.internal.lang.annotation.ajcDeclareAnnotation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.SessionAttributes;
import com.springbook.biz.board.BoardService;
import com.springbook.biz.board.BoardVO;
import com.springbook.biz.board.impl.BoardDAO;
@Controller// web.xml에 있는 프론트컨트롤러를 읽어준다@SessionAttributes("board")// 저장할 때 "board"라는 이름의 객체가 있으면 session 영역에 저장publicclassBoardController{
// 통합 컨트롤러// 변수 선언 - 변수만 선언해서는 아무 정보도 가지고 올 수 없음 // 자동의존주입 방식으로 정보 주입@Autowired// private BoardDAO boardDAO;private BoardService boardService;
// @ModelAttribute 의 특징// @RequestMapping 메소드가 실행 되기 전에 실행된다.// @ModelAttribute 메소드의 실행 결과로 리턴되어지는 객체는 자동으로 Model에 저장된다. // @ModelAttribute 메소드의 실행을 View페이지에서 사용할 수 있다. @ModelAttribute("conditionMap")// ("") <- View 페이지에서 사용할 이름public Map<String, String> searchConditionMap(){
Map<String, String> conditionMap = new HashMap<>();
conditionMap.put("제목", "TITLE");
conditionMap.put("내용", "CONTENT");
return conditionMap;
}
// 글 등록@RequestMapping(value = "/insertBoard.do")public String insertBoard(BoardVO vo){
//boardDAO.insertBoard(vo);
boardService.insertBoard(vo);
return"redirect:getBoardList.do";
}
// 글 수정// @ModelAttribute("board")으로 정보를 가지고 온다 // @SessionAttributes("board")와 실제 같은 값// 수정한 정보는 vo에 들어있다.// 한번 들어온 객체들을 넣어두고 // 수정된 title, content는 나중에 처리// 전체 데이터를 null 데이터 없이 넘겨 처리하기 위해선 이렇게 사용해야 한다.@RequestMapping(value = "/updateBoard.do")public String updateBoard(@ModelAttribute("board") BoardVO vo/* , BoardDAO boardDAO */){
System.out.println(vo.getSeq());
System.out.println(vo.getTitle());
System.out.println(vo.getWriter());
System.out.println(vo.getContent());
System.out.println(vo.getRegDate());
System.out.println(vo.getCnt());
//boardDAO.updateBoard(vo);
boardService.insertBoard(vo);
return"redirect:getBoardList.do";
}
// 글 삭제@RequestMapping(value = "/deleteBoard.do")public String deleteBoard(BoardVO vo){
//boardDAO.deleteBoard(vo);
boardService.insertBoard(vo);
return"redirect:getBoardList.do";
}
// 글 상세조회@RequestMapping(value = "/getBoard.do")public String getBoard(BoardVO vo, Model model){
model.addAttribute("board", boardService.getBoard(vo)); // model은 request 영역에 저장return"getBoard.jsp";
}
// 글 목록 검색// 커멘드 객체인 경우@RequestMapping(value = "/getBoardList.do")public String getBoardList(BoardVO vo, Model model){
model.addAttribute("boardList", boardService.getBoardList(vo));
return"getBoardList.jsp";
}
}
이렇게 하고 실행하면 실행되지 않음.
BoardController의 ‘private BoardService boardService;’ 가 생성되지 않았기 때문.
web.xml 에 추가해야 ‘private BoardService boardService;’ 를 읽어올 수 있다.
메모리에 할당되는 시점 때문에 web.xml에 저렇게 세팅을 해두지 않으면 정상적으로 작동되지 않는다.
원래 컨트롤러에서는 직접 DAO를 커멘드 객체로 만들었다.
이걸 다 지우고 비즈니스 계층으로 만듦. 다형성을 이용하기 위한 것.
기존의 방식으로 하면 수정, 유지보수가 어렵다. DAO를 쓴 부분을 다 수정해야 하기 때문이다.
BoardComtroller.java
// 변수 선언 - 변수만 선언해서는 아무 정보도 가지고 올 수 없음 // 자동의존주입 방식으로 정보 주입@Autowired// private BoardDAO boardDAO;private BoardService boardService;
이렇게 비즈니스 계층을 만듦으로서 유지 보수가 용이해진다.
수정된 전체 web.xml
<?xml version="1.0" encoding="UTF-8"?><web-appxmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns="http://java.sun.com/xml/ns/javaee"xsi:schemaLocation="http://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"version="2.5"><!-- 한글깨짐 방지 --><filter><filter-name>characterEncoding</filter-name><filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class><init-param><param-name>encoding</param-name><param-value>UTF-8</param-value></init-param></filter><filter-mapping><filter-name>characterEncoding</filter-name><url-pattern>*.do</url-pattern></filter-mapping><context-param><param-name>contextConfigLocation</param-name><param-value>classpath:applicationContext.xml</param-value></context-param><listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class></listener><servlet><servlet-name>action</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><param-name>contextConfigLocation</param-name><param-value>/WEB-INF/config/presentation-layer.xml</param-value></init-param></servlet><servlet-mapping><servlet-name>action</servlet-name><url-pattern>*.do</url-pattern></servlet-mapping></web-app>
검색기능 구현
내용이나 제목으로 검색하면 그에 맞는 목록이 검색되게 해야 한다.
BoardDAO.java
package com.springbook.biz.board.impl;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Repository;
import com.springbook.biz.board.BoardVO;
import com.springbook.biz.common.JDBCUtil;
@Repository("boardDAO")publicclassBoardDAO{
private Connection conn = null;
private PreparedStatement stmt = null;
private ResultSet rs = null;
// SQL 명령어// 상수형태로 만들어 두었다.privatefinal String BOARD_INSERT = " insert into board(seq, title, writer, content) values "
+ " ((select nvl(max(seq),0) + 1 from board), ?, ?, ?)";
privatefinal String BOARD_UPDATE = "update board set title=?, content=? where seq=?";
privatefinal String BOARD_DELETE = "delete board where seq=?";
privatefinal String BOARD_GET = "select * from board where seq=?";
privatefinal String BOARD_LIST = "select * from board order by seq desc";
// 검색기능privatefinal String BOARD_LIST_T = "select * from board where title like '%'||?||'%' order by seq desc";
privatefinal String BOARD_LIST_C = "select * from board where content like '%'||?||'%' order by seq desc";
// 글 등록publicvoidinsertBoard(BoardVO vo){
System.out.println("===> JDBC로 insertBoard() 기능 처리");
try {
conn = JDBCUtil.getConnection();
stmt = conn.prepareStatement(BOARD_INSERT);
stmt.setString(1, vo.getTitle());
stmt.setString(2, vo.getWriter());
stmt.setString(3, vo.getContent());
stmt.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtil.close(stmt, conn);
}
}
// 글 수정publicvoidupdateBoard(BoardVO vo){
System.out.println("===> JDBC로 updateBoard() 기능 처리");
try {
conn = JDBCUtil.getConnection();
stmt = conn.prepareStatement(BOARD_UPDATE);
stmt.setString(1, vo.getTitle());
stmt.setString(2, vo.getContent());
stmt.setInt(3, vo.getSeq());
stmt.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtil.close(stmt, conn);
}
}
// 글 삭제publicvoiddeleteBoard(BoardVO vo){
System.out.println("===> JDBC로 deleteBoard() 기능 처리");
try {
conn = JDBCUtil.getConnection();
stmt = conn.prepareStatement(BOARD_DELETE);
stmt.setInt(1, vo.getSeq());
stmt.executeUpdate();
} catch (Exception e){
e.printStackTrace();
} finally {
JDBCUtil.close(stmt, conn);
}
}
// 글 상세 조회public BoardVO getBoard(BoardVO vo){
BoardVO board = new BoardVO();
System.out.println("===> JDBC로 getBoard() 상세 보기 처리");
try {
conn = JDBCUtil.getConnection();
stmt = conn.prepareStatement(BOARD_GET);
stmt.setInt(1, vo.getSeq());
rs = stmt.executeQuery();
if (rs.next()) {
board.setSeq(rs.getInt("SEQ"));
board.setTitle(rs.getString("TITLE"));
board.setWriter(rs.getString("WRITER"));
board.setContent(rs.getString("CONTENT"));
board.setRegDate(rs.getDate("REGDATE"));
board.setCnt(rs.getInt("CNT"));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtil.close(rs, stmt, conn);
}
return board;
}
// 글 목록 조회public List<BoardVO> getBoardList(BoardVO vo){
System.out.println("==> JDBC로 getBoardList() 기능 처리");
List<BoardVO> boardlist = new ArrayList<>();
try {
conn = JDBCUtil.getConnection();
// 검색 기능if (vo.getSearchCondition().equals("TITLE")) {
stmt = conn.prepareStatement(BOARD_LIST_T);
} elseif (vo.getSearchCondition().equals("CONTENT")) {
stmt = conn.prepareStatement(BOARD_LIST_C);
}
stmt.setString(1, vo.getSearchKeyword());
rs = stmt.executeQuery();
while (rs.next()) {
BoardVO board = new BoardVO();
board.setSeq(rs.getInt("SEQ"));
board.setTitle(rs.getString("TITLE"));
board.setWriter(rs.getString("WRITER"));
board.setContent(rs.getString("CONTENT"));
board.setRegDate(rs.getDate("REGDATE"));
board.setCnt(rs.getInt("CNT"));
boardlist.add(board);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtil.close(stmt, conn);
}
return boardlist;
}
}
BoardDAOSpring.java - 실질적으로 현재 연결된 DAO
package com.springbook.biz.board.impl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import com.springbook.biz.board.BoardVO;
// 비즈니스 계층에서 사용할 수 있도록 빈에 등록@RepositorypublicclassBoardDAOSpring{
@Autowiredprivate JdbcTemplate jdbcTemplate;
// 상수형태로 만들어 두었다.privatefinal String BOARD_INSERT = " insert into board(seq, title, writer, content) values "
+ " ((select nvl(max(seq),0) + 1 from board), ?, ?, ?)";
privatefinal String BOARD_UPDATE = "update board set title=?, content=? where seq=?";
privatefinal String BOARD_DELETE = "delete board where seq=?";
privatefinal String BOARD_GET = "select * from board where seq=?";
privatefinal String BOARD_LIST = "select * from board order by seq desc";
// 검색기능privatefinal String BOARD_LIST_T = "select * from board where title like '%'||?||'%' order by seq desc";
privatefinal String BOARD_LIST_C = "select * from board where content like '%'||?||'%' order by seq desc";
// ---------- update() 구문 사용 ----------// 글 등록publicvoidinsertBoard(BoardVO vo){
System.out.println("===> springJDBC로 insertBoard() 기능 처리");
// insert를 springJDBC에서는 update가 처리, try catch문 사용 안해도 됨.// 기존 jdbc 에서 사용한 '?' 대신 순서대로 객체를 가지고 와 적어준다.// jdbcTemplate.update(BOARD_INSERT, vo.getTitle(), vo.getWriter(), vo.getContent());
jdbcTemplate.update(BOARD_INSERT, vo.getTitle(), vo.getWriter(), vo.getContent());
}
// 글 수정publicvoidupdateBoard(BoardVO vo){
System.out.println("===> springJDBC로 updateBoard() 기능 처리");
jdbcTemplate.update(BOARD_UPDATE, vo.getTitle(), vo.getContent(), vo.getSeq());
}
// 글 삭제publicvoiddeleteBoard(BoardVO vo){
System.out.println("===> springJDBC로 deleteBoard() 기능 처리");
jdbcTemplate.update(BOARD_DELETE, vo.getSeq());
}
// ---------- queryForObject(), query() 구문 사용 ----------// 글 상세 조회public BoardVO getBoard(BoardVO vo){
System.out.println("===> springJDBC로 getBoard() 상세 보기 처리");
// 배열로 값을 받기 때문에 배열에 넣어준다
Object[] args= {vo.getSeq()};
// return jdbcTemplate.queryForObject(sql, args, rowMapper);return jdbcTemplate.queryForObject(BOARD_GET, args, new BoardRowMapper());
}
// 글 목록 조회public List<BoardVO> getBoardList(BoardVO vo){
System.out.println("==> springJDBC로 getBoardList() 기능 처리");
Object[] args = {vo.getSearchKeyword()};
if (vo.getSearchCondition().equals("TITLE")) {
return jdbcTemplate.query(BOARD_LIST_T, args, new BoardRowMapper());
} elseif (vo.getSearchCondition().equals("CONTENT")) {
return jdbcTemplate.query(BOARD_LIST_C, args, new BoardRowMapper());
}
returnnull;
}
}
BoardController.java
package com.springbook.view.board;
import java.util.HashMap;
import java.util.Map;
import org.aspectj.internal.lang.annotation.ajcDeclareAnnotation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.SessionAttributes;
import com.springbook.biz.board.BoardService;
import com.springbook.biz.board.BoardVO;
import com.springbook.biz.board.impl.BoardDAO;
@Controller// web.xml에 있는 프론트컨트롤러를 읽어준다@SessionAttributes("board")// 저장할 때 "board"라는 이름의 객체가 있으면 session 영역에 저장publicclassBoardController{
// 통합 컨트롤러// 변수 선언 - 변수만 선언해서는 아무 정보도 가지고 올 수 없음 // 자동의존주입 방식으로 정보 주입@Autowiredprivate BoardService boardService;
// @ModelAttribute 의 특징// @RequestMapping 메소드가 실행 되기 전에 실행된다.// @ModelAttribute 메소드의 실행 결과로 리턴되어지는 객체는 자동으로 Model에 저장된다. // @ModelAttribute 메소드의 실행을 View페이지에서 사용할 수 있다. @ModelAttribute("conditionMap")// ("") <- View 페이지에서 사용할 이름public Map<String, String> searchConditionMap(){
Map<String, String> conditionMap = new HashMap<>();
conditionMap.put("제목", "TITLE");
conditionMap.put("내용", "CONTENT");
return conditionMap;
}
@RequestMapping(value = "/insertBoard.do")public String insertBoard(BoardVO vo){
//boardDAO.insertBoard(vo);
boardService.insertBoard(vo);
return"redirect:getBoardList.do";
}
// 글 수정// @ModelAttribute("board")으로 정보를 가지고 온다 // @SessionAttributes("board")와 실제 같은 값// 수정한 정보는 vo에 들어있다.// 한번 들어온 객체들을 넣어두고 // 수정된 title, content는 나중에 처리// 전체 데이터를 null 데이터 없이 넘겨 처리하기 위해선 이렇게 사용해야 한다.@RequestMapping(value = "/updateBoard.do")public String updateBoard(@ModelAttribute("board") BoardVO vo){
System.out.println(vo.getSeq());
System.out.println(vo.getTitle());
System.out.println(vo.getWriter());
System.out.println(vo.getContent());
System.out.println(vo.getRegDate());
System.out.println(vo.getCnt());
boardService.insertBoard(vo);
return"redirect:getBoardList.do";
}
// 글 삭제@RequestMapping(value = "/deleteBoard.do")public String deleteBoard(BoardVO vo){
boardService.insertBoard(vo);
return"redirect:getBoardList.do";
}
// 글 상세조회@RequestMapping(value = "/getBoard.do")public String getBoard(BoardVO vo, Model model){
model.addAttribute("board", boardService.getBoard(vo)); // model은 request 영역에 저장return"getBoard.jsp";
}
// 글 목록 검색// 커멘드 객체인 경우@RequestMapping(value = "/getBoardList.do")public String getBoardList(BoardVO vo, Model model){
if(vo.getSearchCondition() == null) {
vo.setSearchCondition("TITLE");
}
if(vo.getSearchKeyword() == null) {
vo.setSearchKeyword("");
}
model.addAttribute("boardList", boardService.getBoardList(vo));
return"getBoardList.jsp";
}
}
getBoardList.jsp 에서 searchCondition 스펠링 수정, BoardVO에서도 수정해줬다.
<?xml version="1.0" encoding="UTF-8"?><beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd"><!-- 스캔태그를 사용해서 스캔처리, 모두 어노테이션으로 해결 --><context:component-scanbase-package="com.springbook.view"></context:component-scan><!-- 파일업로드 빈 설정, 빈 등록 시 id는 스프링이 읽을 수 있는 방식으로 정해져 있다.--><beanid="multipartResolver"class="org.springframework.web.multipart.commons.CommonsMultipartResolver"><propertyname="maxUploadSize"value="100000"></property></bean></beans>
BoardVO.java
getOriginalFilename(), transferTo(), isEmpyt() 메소드를 사용해 파일 업로드
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<!-- 스캔태그를 사용해서 스캔처리, 모두 어노테이션으로 해결 -->
<context:component-scan base-package="com.springbook.view"></context:component-scan>
<!-- HttpMessageConverter를 구현한 모든 변환기가 생성된다 -->
<!-- 자바객체를 JSON응답 보디로 변환할 때는 MappingJackson2HttpMessageConverter를 사용한다 -->
<!-- 스프링 컨테이너가 어노테이션 처리를 해준다, 우리가 직접 MappingJackson2HttpMessageConverter을 사용하는 것이 아님.-->
<mvc:annotation-driven></mvc:annotation-driven>
<!-- 파일업로드 빈 설정, 빈 등록 시 id는 스프링이 읽을 수 있는 방식으로 정해져 있다.-->
<bean id="multipartResolver"class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="100000"></property>
</bean>
</beans>
제이슨 형식으로 글 목록을 변환 받은 것을 보기 위해 index.jsp에 a 태그를 추가했다.
index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html><html><head><metacharset="UTF-8"><title>Insert title here</title></head><body><center><h1>게시판 프로그램</h1><hr><ahref="login.do">로그인</a><br><br><ahref="getBoardList.do">글 목록 바로가기</a><br><br><ahref="dataTransform.do">글 목록 변환</a><hr></center></body></html>
BoardController.java
package com.springbook.view.board;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
//import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.multipart.MultipartFile;
import com.springbook.biz.board.BoardService;
import com.springbook.biz.board.BoardVO;
//import com.springbook.biz.board.impl.BoardDAO;@Controller// web.xml에 있는 프론트컨트롤러를 읽어준다@SessionAttributes("board")// 저장할 때 "board"라는 이름의 객체가 있으면 session 영역에 저장publicclassBoardController{
// 통합 컨트롤러// 변수 선언 - 변수만 선언해서는 아무 정보도 가지고 올 수 없음 // 자동의존주입 방식으로 정보 주입@Autowired// private BoardDAO boardDAO;private BoardService boardService;
// 글 목록 JSON으로 변환@RequestMapping(value = "/dataTransform.do")// @ResponseBody 어노테이션을 사용하여 자바 객체를 HTTP 응답 본문의 객체로 변환하며 클라이언트로 전송한다@ResponseBody// 자바형식을 제이슨형식으로 바꿔달라고 요청public List<BoardVO> dataTransform(BoardVO vo){
vo.setSearchCondition("TITLE");
vo.setSearchKeyword("");
List<BoardVO> boardList = boardService.getBoardList(vo);
return boardList;
}
// @ModelAttribute 의 특징// @RequestMapping 메소드가 실행 되기 전에 실행된다.// @ModelAttribute 메소드의 실행 결과로 리턴되어지는 객체는 자동으로 Model에 저장된다. // @ModelAttribute 메소드의 실행을 View페이지에서 사용할 수 있다. @ModelAttribute("conditionMap")// ("") <- View 페이지에서 사용할 이름public Map<String, String> searchConditionMap(){
Map<String, String> conditionMap = new HashMap<>();
conditionMap.put("제목", "TITLE");
conditionMap.put("내용", "CONTENT");
return conditionMap;
}
// 글 등록 @RequestMapping(value = "/insertBoard.do")public String insertBoard(BoardVO vo)throws IllegalStateException, IOException { // BoardVO vo가 커멘드 객체//boardDAO.insertBoard(vo);// 파일 업로드
MultipartFile uploadFile = vo.getUploadFile();
if (!uploadFile.isEmpty()) {
String fileName = uploadFile.getOriginalFilename();
uploadFile.transferTo(new File("c://work//uploads//" + fileName)); // 파일 위치 경로
}
boardService.insertBoard(vo);
return"redirect:getBoardList.do";
}
// 글 수정// @ModelAttribute("board")으로 정보를 가지고 온다 // @SessionAttributes("board")와 실제 같은 값// 수정한 정보는 vo에 들어있다.// 한번 들어온 객체들을 넣어두고 // 수정된 title, content는 나중에 처리// 전체 데이터를 null 데이터 없이 넘겨 처리하기 위해선 이렇게 사용해야 한다.@RequestMapping(value = "/updateBoard.do")public String updateBoard(@ModelAttribute("board") BoardVO vo/* , BoardDAO boardDAO */){
System.out.println(vo.getSeq());
System.out.println(vo.getTitle());
System.out.println(vo.getWriter());
System.out.println(vo.getContent());
System.out.println(vo.getRegDate());
System.out.println(vo.getCnt());
//boardDAO.updateBoard(vo);
boardService.updateBoard(vo);
return"redirect:getBoardList.do";
}
// 글 삭제@RequestMapping(value = "/deleteBoard.do")public String deleteBoard(BoardVO vo/* , BoardDAO boardDAO */){
//boardDAO.deleteBoard(vo);
boardService.deleteBoard(vo);
return"redirect:getBoardList.do";
}
// 글 상세조회@RequestMapping(value = "/getBoard.do")public String getBoard(BoardVO vo/* , BoardDAO boardDAO */, Model model){
model.addAttribute("board", boardService.getBoard(vo)); // model은 request 영역에 저장return"getBoard.jsp";
}
// 글 목록 검색// 커멘드 객체인 경우@RequestMapping(value = "/getBoardList.do")public String getBoardList(BoardVO vo/* , BoardDAO boardDAO */, Model model){
if(vo.getSearchCondition() == null) {
vo.setSearchCondition("TITLE");
}
if(vo.getSearchKeyword() == null) {
vo.setSearchKeyword("");
}
model.addAttribute("boardList", boardService.getBoardList(vo));
return"getBoardList.jsp";
}
// 글 목록 검색// 커멘드 객체가 아닌 경우/*
* @RequestMapping(value = "/getBoardList.do") public String
* getBoardList(@RequestParam(value = "searchCondtion",defaultValue =
* "TITLE",required = false) String condition,
*
* @RequestParam(value = "searchKeyword",defaultValue = "CONTENT",required =
* false) String keyword, BoardVO vo, BoardDAO boardDAO, Model model) {
*
* System.out.println("검색 조건" + condition); System.out.println("검색 단어" +
* keyword);
*
* model.addAttribute("boardList", boardDAO.getBoardList(vo)); return
* "getBoardList.jsp"; }
*/
}
맥북에도 스프링을 깔고 기존에 쓴 코드들을 다 가지고 왔다.
실행해보니 글이 삭제되지 않아서 보니까 BoardController에 오타가 있어서 수정했다.