강아지를 키우는 사람들은 의료 정보와 애견 동반 가능한 장소에 대한 정보 접근이 어려운 경우가 많습니다. 이러한 문제를 해결하기 위해 강아지 병원 정보, 애견 동반 가능한 휴가 장소 추천, 그리고 내 강아지를 자랑할 수 있는 커뮤니티 기능을 제공하는 웹사이트를 기획하게 되었습니다.
의의
지금은 586만 마리의 강아지와 함께 하는 시대!
국민은행 경영연구소가 발표한 2023 한국 반려동물 보고서에 따르면,
현재 대한민국에는 582만 마리 이상의 강아지가 살고 있습니다.
가족처럼 소중한 반려동물과 건강하고 오래 살고자 하는 반려가구가 늘어나면서,
다양한 건강 관리 정보에 대한 필요성도 증가하고 있습니다.
이 웹사이트는 이러한 반려가구가 보다 성숙한 반려견 양육 문화를 형성하는 데 기여하고자 합니다.
package com.dino.root;
import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;
import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
/**
* Handles requests for the application home page.
*/
@Controller
public class RootController {
private static final Logger logger = LoggerFactory.getLogger(RootController.class);
/**
* Simply selects the home view to render by returning its name.
*/
@RequestMapping(value = "/", method = RequestMethod.GET)
public String home(Locale locale, Model model) {
logger.info("Welcome home! The client locale is {}.", locale);
Date date = new Date();
DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);
String formattedDate = dateFormat.format(date);
model.addAttribute("serverTime", formattedDate );
return "home";
}
@RequestMapping(value = "signup", method = RequestMethod.GET)
public String signup() {
System.out.println("signup");
return "signup";
}
@RequestMapping(value = "signup", method = RequestMethod.POST)
public String signup2(HttpServletRequest req) {
System.out.println("signup 회원가입 정보를 저장");
System.out.println(req.getParameter("aaa"));
return "home";
}
}
homecontroller 에서 수정된 RootController.java
package com.dino.root;
import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
public class RootController {
private static final Logger logger = LoggerFactory.getLogger(RootController.class);
// 메인 페이지 이동
@RequestMapping(value = "/", method = RequestMethod.GET)
public String home() {
logger.info("Welcome to the home page!");
return "home";
}
// 로그인 페이지 이동
@RequestMapping(value = "/login", method = RequestMethod.GET)
public String login() {
return "login";
}
// 로그인 요청 처리
@RequestMapping(value = "/login", method = RequestMethod.POST)
public String loginPost(@RequestParam("userid") String userid,
@RequestParam("password") String password,
HttpServletRequest req) {
// 아이디와 비밀번호 확인 (예제로 확인용)
if ("user".equals(userid) && "password".equals(password)) {
return "redirect:/game";
} else {
return "login";
}
}
// 회원가입 페이지 이동
@RequestMapping(value = "/signup", method = RequestMethod.GET)
public String signup() {
return "signup";
}
// 회원가입 요청 처리
@RequestMapping(value = "/signup", method = RequestMethod.POST)
public String signupPost(HttpServletRequest req) {
// 회원가입 처리 로직 (여기서는 예제로 간단하게 출력만 함)
String userid = req.getParameter("userid");
String password = req.getParameter("password");
logger.info("회원가입한 회원 이름: {} 비밀번호: {}", userid, password);
// 회원가입 후 바로 로그인 처리
if ("user".equals(userid) && "password".equals(password)) {
return "redirect:/game";
} else {
return "login";
}
}
// 랭킹 페이지 이동
@RequestMapping(value = "/ranking", method = RequestMethod.GET)
public String ranking() {
return "ranking";
}
// 게임 페이지 이동 (로그인 성공 후)
@RequestMapping(value = "/game", method = RequestMethod.GET)
public String game() {
return "game";
}
}
private static final Logger logger = LoggerFactory.getLogger(RootController.class);
logger.info → syso 는 단순 문자열출력으로 확인만 했다면 log 가 더 자세하게 오류 원인을 알려준다고 해서 사용해보았다. (프로젝트 만들면 기본으로 homecontroller에 샘플코드로 찍히는데 log 관련된 것 같길래 구글링해보니 이렇게 사용하면 된다고 해서 사용해본 것)
미니프로젝트였지만 스프링을 다 이해하고 돌입하게 아니어서 파일구조부터 컨트롤러 구성까지 시행착오가 많았고, 시행착오가 많았던 만큼 스프링에 대해 공부도 많이 됐던 프로젝트다. 사실 여전히 깃은 잘 모르겠지만.. 모르는 만큼 본격적인 프로젝트에 들어가기 전 공부를 해야겠다고 다짐한다! ㅠ
또, 앞으로는 얼마 남지 않은 기간이지만 날마다 그날 작성한 코드들을 압축해 관리하는 습관을 들여야할것 같다.
그렇게 해놓지 않으니 스프링 JDBC 방식이 뭐였는지 mybatis가 뭐였는지 구별이 안된다. 둘을 제대로 알고 넘어간게 아니라 하날 공부하는 와중에 다른 걸 또 배우니 머리 속에서 정리가 안되는 거다. 그래도 시험을 보며 잘 정리할 기회를 얻어서 다행.. 블로그에도 두 방식을 나눠 정리를 잘 해둬야겠다.
이런저런 팀원들의 사정으로 내가 거의 다 해냈던 프로젝트인데 그래서인지 애정이 정말 크다.
내가 만든 프로젝트라고 자신있게 말할 수 있을 것 같다. 아주 작은 규모라도 끝을 냈다는 것에 의의를 두고 자신감을 가져야겠다. 회고는 이쯤에서 끝내고 3~4일 가량이었지만 고생했던 프로젝트를 끝냄 기념으로 기록해보려 한다.
프로젝트 주제 : 공룡게임을 할 수 있는 사이트
간단한 조작을 통해 누구나 공룡게임을 즐길 수 있고, 회원가입을 하면 본인의 랭킹을 확인할 수 있습니다.
기간 ) 0801 ~ 0804
팀원 ) 강유나, ____, ____
역할 )
강유나 : 컨트롤러 구현, 회원가입 DAO, VO, Service 기능 구현, jsp파일 css, html, javascript 공룡 게임 구현, 파일 정의서, 화면 정의서 작성
____ : jsp파일 css, html 디자인, 공룡 게임 수정, 팀원에게 깃 사용법 전파
____ : 랭킹 페이지 구현
☄️ 구현할 페이지 : 게임을 할 수 있는 메인화면, 로그인, 회원가입, 랭킹 확인
패키지/ 자바 파일 정의서
shoppingmall 폴더에서 작업 ) -> 초기에는 shoppingmall 이름으로 프로젝트를 모든 조가 일괄적으로 만들었기 때문에 이름이 이렇다.
src > main > wabapp 에는 화면을 띄울 jsp 파일이 들어갑니다.
index.jsp → 메인 화면으로 바로 공룡게임을 할 수 있는 화면이 보여집니다.
login.jsp → 로그인 화면입니다.
memberjoin.jsp → 회원가입 화면입니다.
ranking.jsp → 회원들의 게임 점수를 기반으로 랭킹이 매겨집니다.
자바 파일 및 JSP 파일 정의서 수정
각 기능은 동일하지만 보다 정리된 파일 구조입니다.
-> 컨트롤러를 이해하고 훨씬 더 간결해진 파일 구조다.
sql deverloper 에서 작업 )
kit3 계정을 만들어 회원 정보를 담을 테이블과 회원의 점수를 담을 테이블 생성
reate table member(
id varchar2(15) primary key,
pass varchar2(20) not null,
name varchar2(200) not null
);
create table board(
id varchar2(200) primary key,
score number not null,
content varchar2(200),
num number
);
alter table board
add constraint board_mem_fk foreign key (id)
references member (id);
create sequence seq_board_num2
increment by 1
start with 1
minvalue 1;
select id,score,content
from (select * from board order by score desc)
where rownum <=20 ;
화면정의서
메인 화면 ( index.jsp )
로그인 화면 ( login.jsp )
회원가입 화면 ( memberJoin.jsp )
랭킹 화면 ( ranking.jsp )
☄️ 실제 구현 화면
함께 공유해요
-> 프로젝트를 하며 새롭게 알게됐던 부분, 함께 나누고 싶은 부분을 '함께 공유해요'라는 이름으로 발표해보았다.
스프링에서 정적자원 삽입하기
스프링에서 css, js, image 등의 자원은 파일이 존재하는 url 그 자체로 사용된다. 따라서 url 요청을 해야 하는데 이는 MVC의 DispatcherServlet에서 판단해 Controller에서 RequestMapping Annotation을 검색하게 된다. 이 때 404에러가 발생하게 된다.
따라서 CSS, JavaScript, Image 같은 정적 자원들에 대해 URL을 따로 주어야 하는데 이를 지원해주는 녀석이 mvc:resources이다. 이 태그를 DispatcherServlet에 등록해줘야 사용할 수 있다.
어떻게 game jsp에 자바스크립트를 넣었는지에 대한 자세한 설명 참조
공룡게임 참고 - 코딩애플 유튜브영상
공룡게임 수정
로그인을 실행하게 될 경우, 바로 게임 화면으로 이동하면서 게임이 시작되는 부분이 있었다. 이것은 유저의 순발력을 요구하는 플레이가 될 수 있지만, 실제로 게임을 즐기는 유저 입장에서는 굉장히 불쾌할 수 있는 요소중 하나이다. 적어도 게임에 입장하게 되면, 바로 조작할 수 있게 진행해주었어야했었다고 생각했다.
참고 문서 :
• 이 속성은 문서의 탭 순서 안에서 현재 요소의 순번을 결정한다. 값은 0부터 32767 사이의 숫자여야 한다. 브라우저는 값 앞에 0이 붙어있으면 무시해야 한다.
<form action="login.do" method="post"> 를 DispatcherServlet에 써 놓은 주소로 바꿔준다.
DispatcherServlet 에는 login.do로 페이지를 이동하면 어느 화면을 보여줄지 판단하기 위해 모델1방식에서 작성했던 login_proc 의 코드를 옮겨 넣어준다.
모델 1방식에서는 프론트컨트롤러 없이 login_proc가 바로 화면 이동을 할 수 있게 해줬다.
→ 글 목록 보기
http://localhost:9999/BoardWeb/getBoardList.jsp 이 주소로 직접 실행하면 안되고
http://localhost:9999/BoardWeb/getBoardList.do 로 직접 접근해야한다.
이렇게 접근해도 주소는 .jsp로 바뀌는데 리다이렉트 방식이기 때문
DispatcherServlet
package com.springbook.view.controller;
import java.io.IOException;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import com.springbook.biz.board.BoardVO;
import com.springbook.biz.board.impl.BoardDAO;
import com.springbook.biz.user.UserVO;
import com.springbook.biz.user.impl.UserDAO;
// @WebServlet("*.do") 라고 쓰는 매핑정보가 web.xml에 들어 있다.
// 프론트 컨트롤러의 역할
public class DispatcherServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
process(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
process(request, response);
}
private void process(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// request, response를 받아서 필요한 작업 처리
// getRequestURI() 포트번호를 제외하고 뒤의 값을 반환
String uri = request.getRequestURI();
String path = uri.substring(uri.lastIndexOf("/")); // /login.do <- 이렇게 '/' 뒤의 주소를 가지고 온다
System.out.println(path);
// .do 뒤에 뭐가 오냐에 따라 판단
if (path.equals("/login.do")) {
System.out.println("로그인 처리");
// login_proc 의 코드. 모델 2에서는 프론트컨트롤러가 proc의 역할을 수행한다.
String id = request.getParameter("id");
String password = request.getParameter("password");
UserVO vo = new UserVO();
vo.setId(id);
vo.setPassword(password);
UserDAO dao = new UserDAO();
UserVO user = dao.getUser(vo);
if(user != null) {
HttpSession session = request.getSession();
session.setAttribute("id", vo.getId());
response.sendRedirect("getBoardList.do");
}else{
response.sendRedirect("login.jsp");
}
} else if (path.equals("/logout.do")) {
System.out.println("로그아웃 처리");
HttpSession session = request.getSession();
session.invalidate();
response.sendRedirect("login.jsp");
} else if (path.equals("/insertBoard.do")) {
System.out.println("글 등록 처리");
request.setCharacterEncoding("UTF-8");
String title = request.getParameter("title");
String writer = request.getParameter("writer");
String content = request.getParameter("content");
BoardVO vo = new BoardVO();
BoardDAO dao = new BoardDAO();
vo.setTitle(title);
vo.setWriter(writer);
vo.setContent(content);
dao.insertBoard(vo);
response.sendRedirect("getBoardList.do");
} else if (path.equals("/updataBoard.do")) {
System.out.println("글 수정 처리");
request.setCharacterEncoding("UTF-8");
String seq = request.getParameter("seq");
String title = request.getParameter("title");
String content = request.getParameter("content");
BoardVO vo = new BoardVO();
BoardDAO dao = new BoardDAO();
vo.setSeq(Integer.parseInt(seq));
vo.setTitle(title);
vo.setContent(content);
dao.updateBoard(vo);
response.sendRedirect("getBoardList.do");
} else if (path.equals("/deleteBoard.do")) {
System.out.println("글 삭제 처리");
request.setCharacterEncoding("UTF-8");
String seq = request.getParameter("seq");
BoardVO vo = new BoardVO();
BoardDAO dao = new BoardDAO();
vo.setSeq(Integer.parseInt(seq));
dao.deleteBoard(vo);
response.sendRedirect("getBoardList.do");
} else if (path.equals("/getBoard.do")) {
System.out.println("글 상세 조회 처리");
String seq = request.getParameter("seq");
BoardVO vo = new BoardVO();
BoardDAO dao = new BoardDAO();
vo.setSeq(Integer.parseInt(seq));
BoardVO board = dao.getBoard(vo);
// 데이터를 session에 담아 최종적으로 "getBoard.jsp"로 페이지 이동
HttpSession session = request.getSession();
session.setAttribute("board", board);
response.sendRedirect("getBoard.jsp");
} else if (path.equals("/getBoardList.do")) {
System.out.println("글 목록 검색 처리");
BoardVO vo = new BoardVO();
BoardDAO dao = new BoardDAO();
List<BoardVO> boardList = dao.getBoardList(vo);
// 목록을 session에 담아 최종적으로 "getBoardList.jsp"로 페이지 이동
HttpSession session = request.getSession();
session.setAttribute("boardList", boardList);
// 실제 화면에 뿌려줄때는 jsp로 가야한다.
response.sendRedirect("getBoardList.jsp");
}
}
}
<?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"
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">
<!-- 패키지를 스캔해준다. 다 해주는 것은 아니고 표시되어진 클래스만 스캔해준다 -> 이것이 어노테이션 -->
<!-- <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>
</beans>
BoardDAOSpring
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 class BoardDAOSpring {
@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";
// ---------- update() 구문 사용 ----------
// 글 등록
public void insertBoard(BoardVO vo) {
System.out.println("===> springJDBC로 insertBoard() 기능 처리");
// insert를 springJDBC에서는 update가 처리, try catch문 사용 안해도 됨.
// 기존 jdbc 에서 사용한 '?' 대신 순서대로 객체를 가지고 와 적어준다.
jdbcTemplate.update(BOARD_INSERT, vo.getTitle(), vo.getWriter(), vo.getContent());
}
// 글 수정
public void updateBoard(BoardVO vo) {
System.out.println("===> springJDBC로 updateBoard() 기능 처리");
jdbcTemplate.update(BOARD_UPDATE, vo.getTitle(), vo.getContent(), vo.getSeq());
}
// 글 삭제
public void deleteBoard(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() 기능 처리");
// '?' 가 없다. 글 목록 전체 조회이기 때문에 모든 글들을 다 불러오면 된다.
return jdbcTemplate.query(BOARD_LIST, new BoardRowMapper());
}
}
BoardRowMapper
package com.springbook.biz.board.impl;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.springframework.jdbc.core.RowMapper;
import com.springbook.biz.board.BoardVO;
public class BoardRowMapper implements RowMapper<BoardVO> {
// BoardDAO 의 resultSet의 역할을 수행한다.
@Override
public BoardVO mapRow(ResultSet rs, int rowNum) throws SQLException {
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"));
return board;
}
}
<?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"
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">
<!-- 패키지를 스캔해준다. 다 해주는 것은 아니고 표시되어진 클래스만 스캔해준다 -> 이것이 어노테이션 -->
<!-- <context:component-scan base-package="polymorphism"></context:component-scan> -->
<context:component-scan base-package="com.springbook.biz"></context:component-scan>
<!-- <bean id="log" class="com.springbook.biz.common.LogAdvice" ></bean> -->
<bean id="log" class="com.springbook.biz.common.Log4jAdvice" ></bean>
<bean id="around" class="com.springbook.biz.common.AroundAdvice" ></bean>
<aop:config>
<aop:pointcut expression="execution(* com.springbook.biz..*Impl.*(..))" id="allPointcut"/>
<aop:aspect ref="around"> <!-- 동작시점 -->
<!-- 대상, 적용할 기능 -->
<aop:around pointcut-ref="allPointcut" method="aroundLog"/>
</aop:aspect>
</aop:config>
<!-- = SamsungTV tv = new SamsungTV(); -->
<!-- <bean id="" class=""></bean> -->
<!-- <bean id="tv" class="polymorphism.SamsungTV"></bean> 싱글톤 패턴 사용 -->
<!-- scope="prototype"을 추가하면 싱글톤 패턴 사용을 하지 않을 수 있다. -->
<!-- 생성자 의존 주입 -->
<!--<bean id="tv" class="polymorphism.SamsungTV" scope="prototype"> -->
<!-- 괸계설정 -->
<!-- <constructor-arg ref="apple"></constructor-arg> -->
<!-- 기본형 관계설정 시 value를 쓴다 -->
<!-- <constructor-arg value="10000"></constructor-arg> -->
<!-- </bean> -->
<!-- Setter 메소드 의존 주입 -->
<!-- <bean id="tv" class="polymorphism.SamsungTV" scope="prototype">
<property name="speaker" ref="sony"></property>
<property name="price" value="10000"></property>
</bean>
<bean id="sony" class="polymorphism.SonySpeaker"></bean>
<bean id="apple" class="polymorphism.AppleSpeaker"></bean> -->
</beans>
스프링의 3대 요소
AOP (Aspect Oriented Programmig)
: 관심지향 (공통 관심사)
로깅, 보안, 트랜잭션 관리 등과 같은 공통적인 관심사를 모듈화 해 코드 중복을 줄이고 유지 보수성을 형성하는데 도움을 준다.
공통관심사는 대상기능의 실행 전, 후로 나뉜다.
로그에 대해 이해하기
LogAdvice.java를 수행할 땐 Log4jAdvice.java 메소드를 주석 처리,
Log4jAdvice.java를 실행할 땐 LogAdvice.java 메소드를 주석 처리해야한다.
LogAdvice.java
package com.springbook.biz.common;
// --------------- 공통관심사 ---------------
public class LogAdvice {
public void printLog() {
System.out.println("[공통로그] 비즈니스 로직 수행 전");
}
}
// --------------------------------------
Log4jAdvice.java 심화된 공통 관심사
package com.springbook.biz.common;
public class Log4jAdvice {
public void printLogging() {
System.out.println("[공통로그 - Log4j] 비즈니스 로직 수행 전 동작");
}
}
BoardServiceImpl.java
package com.springbook.biz.board.impl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.springbook.biz.board.BoardService;
import com.springbook.biz.board.BoardVO;
import com.springbook.biz.common.Log4jAdvice;
import com.springbook.biz.common.LogAdvice;
@Service("boardService")
public class BoardServiceImpl implements BoardService {
// boardDAO를 의존 주입해야 한다.
@Autowired
private BoardDAO boardDAO;
private Log4jAdvice log;
public BoardServiceImpl() {
log = new Log4jAdvice();
}
// private LogAdvice log;
//
// public BoardServiceImpl() {
// log = new LogAdvice();
// }
@Override
public void insertBoard(BoardVO vo) {
// @Autowired를 했기 때문에 사용가능, @Autowired로 객체를 생성한거다.
//log.printLog();
log.printLogging();
boardDAO.insertBoard(vo);
}
@Override
public void updateBoard(BoardVO vo) {
//log.printLog();
log.printLogging();
boardDAO.updateBoard(vo);
}
@Override
public void deleteBoard(BoardVO vo) {
//log.printLog();
log.printLogging();
boardDAO.deleteBoard(vo);
}
@Override
public BoardVO getBoard(BoardVO vo) {
//log.printLog();
log.printLogging();
return boardDAO.getBoard(vo);
}
@Override
public List<BoardVO> getBoardList(BoardVO vo) {
//log.printLog();
log.printLogging();
return boardDAO.getBoardList(vo);
}
}
<aop:config>
<!-- 실행 범위 -->
<aop:pointcut expression="execution(* com.springbook.biz..*Impl.*(..))" id="allPointcut"/>
<!-- 레퍼런스 log -->
<aop:aspect ref="log">
<!-- 대상, 적용할 기능 -->
<aop:before pointcut-ref="allPointcut" method="printLog"/>
</aop:aspect>
</aop:config>
"execution(* com.springbook.biz..*Impl.*(..))"
= 패키지 하위의 범위를 표현하는 정규식
= 패키지 안에 Impl 이 들어간 파일이 있다면 공통관심사 코드 실행
package com.springbook.biz.common;
import org.aspectj.lang.ProceedingJoinPoint;
public class AroundAdvice {
// 매개변수를 반드시 ProceedingJoinPoint로 써줘야 한다
public Object aroundLog(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("[Before] 비즈니스 로직 수행 전");
// 비즈니스 로직 메소드의 동작을 감지
Object returnObj = pjp.proceed();
System.out.println("[After] 비즈니스 로직 수행 후");
return returnObj;
}
}
applicationContext.xml
<?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"
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">
<!-- 패키지를 스캔해준다. 다 해주는 것은 아니고 표시되어진 클래스만 스캔해준다 -> 이것이 어노테이션 -->
<!-- <context:component-scan base-package="polymorphism"></context:component-scan> -->
<context:component-scan base-package="com.springbook.biz"></context:component-scan>
<!-- <bean id="log" class="com.springbook.biz.common.LogAdvice" ></bean> -->
<bean id="log" class="com.springbook.biz.common.Log4jAdvice" ></bean>
<bean id="around" class="com.springbook.biz.common.AroundAdvice" ></bean>
<aop:config>
<aop:pointcut expression="execution(* com.springbook.biz..*Impl.*(..))" id="allPointcut"/>
<aop:aspect ref="around"> <!-- 동작시점 -->
<!-- 대상, 적용할 기능 -->
<aop:around pointcut-ref="allPointcut" method="aroundLog"/>
</aop:aspect>
</aop:config>
</beans>