|
Back-End/API 2019. 6. 27. 18:19
-HttpServletRequest-
HttpServletRequest를 사용하면, 그 안에 있는 값을 받아올 수 있다.
예를 들어, 아이디, 비밀번호 등의 데이터를 컨트롤러로 보냈을 때,
HttpServletRequest 객체 안에 모든 데이터들이 들어가게 된다.
원하는 데이터를 꺼낼 때는 HttpServletRequest 객체 안의 메소드를 이용하면 된다.
getParameter( ) 메소드는 반환타입이 String 타입이다.
예를 들어,
String id = httpServletRequest.getParameter("id"); |
httpServletRequest안에 있는 id값을 String타입으로 가져온다.
출처 https://hongku.tistory.com/118
==================================================================================================
-HttpServletResponse-
클라이언트에 데이터를 전송하기 위해서 사용함,
이를 위하여 Response 객체의 setContentType( ) 와 getWriter( ) 메소드를 이용한다.
그 다음에 html을 작성하거나 다른 컨텐츠를 기록하거나 하는 I/O 작업을 하면 된다.
이 외에도 헤더 정보를 설정하거나, 오류를 발생시키거나, 쿠키를 추가할 때도 Response 객체를 사용한다.
> jar파일을 전송한다면.. public class CodeReturn extends HttpServlet{ public void doGet(HttpServletRequest request, HttpServletResponse reponse) throws IOException{ response.setContentType("application/jar"); //MIME타입. 외우지말자 ServletContext ctx = getServletContext(); InputStream is = ctx.getResourceAsStream("/book.jar"); // 이 코드를 풀어보면 입력스트림으로 book.jar을 주세요. // 인자값은 반드시 /로 시작. OutputStream os = response.getOutputStream(); byte[] bytes = new byte[1024]; while(true){ int count = is.read(bytes); out.write(bytes,0,count); if(bytes == -1){ break; } } os.flush(); os.close(); }
출처 https://mkkbest.tistory.com/entry/HttpServletRequest-%EA%B0%9D%EC%B2%B4-HttpServletResponse-%EA%B0%9D%EC%B2%B4
Back-End/Spring 2019. 6. 27. 17:56
- Interceptor (인터셉터) -
클라이언트의 요청 전,후에 특정 작업을 처리하고자 할 때 사용하는 기능 (간단하게 컨트롤러 같은 계층이라고 생각하면 됨)
매개변수 - HttpServletRequest, HttpServletResponse 용도 - 로그인 처리, pc웹 / 모바일웹 분기 처리 등
Filter : 인코딩 처리할때 사용 (예를 들면 한글 인코딩 필터), 필터는 선처리만 된다. Interceptor : AOP의 @Around와 비슷한 성격이다, 하지만 AOP는 메소드 단위 (코드)로 설정하고, 인터셉터는 특정한 URL 단위로 설정한다. html 페이지는 웹서버에서 별도의 처리를 하지 않고, 그대로 클라이언트에게 전달된다.
jsp 페이지는 JspServlet를 경유하여 html로 변환된 후 클라이언트에게 전달됩니다. Spring Framework에서는 DispatchereServlet을 경유하여 처리됩니다.
인터셉터는 Filter (필터) - DispatcherServlet 이전에 실행되며 선처리만 가능합니다. AOP - @Around => ProceedingJoinPoint
@Before, @After => JoinPoint
실행순서 : 1. Filter > 2. DispatcherServlet > 3. Interceptor > 4. AOP
-예제 1. 특정한 url에 대해서 인터셉터가 실행되는지 확인.- SampleInterceptor.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | package com.example.spring02.interceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; // HandlerInterceptorAdapter 를 상속받음(추상클래스) public class SampleInterceptor extends HandlerInterceptorAdapter { //로깅을 위한 변수 private static final Logger logger = LoggerFactory.getLogger(SampleInterceptor.class); //선처리와 후처리는 필수가 아니므로 원하는곳에서 작업을 하면 된다. //선처리 @Override //preHandle는 메인 요청 전에 경유하는 메소드 public boolean preHandle(HttpServletRequest request , HttpServletResponse response, Object handler) throws Exception { logger.info("pre handle..."); return true; //true이면 계속 진행, false이면 멈춤 } //후처리 @Override //postHandle는 메인 요청이 끝난 다음에 경유하는 메소드 public void postHandle(HttpServletRequest request , HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { logger.info("post handle..."); } } | cs |
servlet - context 인터셉터 관련 설정
먼저 인터셉터를 bean으로 설정한 후
<beans : bean id = "인터셉터의 아이디" class = "인터셉터의 경로" />
|
어떤 url을 호출했을 때 인터셉터를 작동시킬 것인지 설정해야 합니다.
<interceptors> <interceptor> <mapping path = "/shop/**"/> <beans:ref bean = "인터셉터의 아이디"/> </interceptor> </interceptors>
|
servlet-context.xml (인터셉터 관련 빈을 등록하고 shop 하위 모든 url에 인터셉터를 적용) => 즉, shop 하위 url이 실행될때 인터셉터 빈에 등록된 클래스가 실행된다. | <!-- 인터셉터 빈을 등록 --> <beans:bean id = "sampleInterceptor" class = "com.example.spring02.interceptor.SampleInterceptor" /> <interceptors> <interceptor> <!-- 인터셉터의 매핑 정보 --> <!-- shop 하위 url에 인터셉터를 적용 --> <mapping path="/shop/**" /> <beans:ref bean="sampleInterceptor" /> //아이디를 참조하는 것이기 때문에 bean id와 ref bean은 같아야 한다. </interceptor> </interceptors> | cs |
코드를 추가하고 서버를 돌리고 로그를 확인해보면 pre handle와 post handle 사이에 인터셉터가 실행된 것을 확인할 수 있다.
==============================================================================================
예제 2.
views / include / session_check.jsp
인터셉터를 사용하지 않고 세션의 존재 여부를 체크하는 코드를 작성할 경우 아래와 같이 처리한다.
shop하위의 페이지로 넘어갈때 서블릿에 인터셉터를 빈으로 등록해놓으면 장바구니를 클릭할때
인터셉터에서 로그인 여부를 판단해서 작업을 처리한다.
|
원래는 컨트롤러에서 처리를 해도 된다 관리자 전용 페이지의 세션 체크 http://localhost:8080/spring02/shop/product/write.do
write.jsp파일에 다음 코드를 추가 <%@ page language = "java" contentType = "text/html; charset=UTF-8" pageEncoding = "UTF-8"%> <!--views/include/session_check.jsp --> <%@ taglib prefix = "c" uri = "http://java.sun.com/jsp/jstl/core" %> <c:if test = "${sessionScope.admin_userid == null}" > // 세션을 체크했을때 null값과 같으면 (로그인을 안했다면).. <script> alert("로그인 하신 후 사용하세요."); // 다음 메시지를 출력함.. location.href = "${path}/admin/login.do"; // 로그인 페이지로 이동시켜주는 코드 </script> </c:if>
|
LoginInterceptor.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | package com.example.spring02.interceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; //HandlerInterceptorAdapter 추상클래스 상속 // preHandle(), postHandler() 오버라이딩 public class LoginInterceptor extends HandlerInterceptorAdapter { //로그인을 하기전에 로그인이 되어있는 상태인지 검사하고 로그인이 되어있으면 //메인 액션으로 이동하고, 로그인이 안되어있으면 로그인 페이지로 이동시킨다. //메인 액션이 실행되기 전 실행되는 메소드
@Override public boolean preHandle(HttpServletRequest request , HttpServletResponse response, Object handler) throws Exception { //세션 객체 생성 HttpSession session=request.getSession(); //세션이 없으면(로그인되지 않은 상태) if(session.getAttribute("userid") == null) { //login 페이지로 이동 response.sendRedirect(request.getContextPath() +"/member/login.do?message=nologin"); return false; //메인 액션으로 가지 않음 }else { return true; //메인 액션으로 이동 } } //메인 액션이 실행된 후 실행되는 메소드 @Override public void postHandle(HttpServletRequest request , HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { super.postHandle(request, response, handler, modelAndView); } } | cs |
이 인터셉터를 적용하기 위해 태그를 선언해야 한다.
servlet - context.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | <!-- 인터셉터 빈을 등록 --> <beans:bean id="sampleInterceptor" //id에는 클래스이름에다가 첫글자만 소문자로 된 것을 많이 사용한다. class="com.example.spring02.interceptor.SampleInterceptor"> //클래스에는 정확한 경로를 적어야 한다. </beans:bean> <beans:bean id="loginInterceptor" class="com.example.spring02.interceptor.LoginInterceptor"> </beans:bean> <beans:bean id="adminInterceptor" class="com.example.spring02.interceptor.AdminInterceptor" /> <!-- 인터셉터 호출을 위한 url mapping --> <interceptors> <interceptor> <!-- 인터셉터의 매핑 정보 --> <!-- shop 하위 url에 인터셉터를 적용 --> <mapping path="/shop/**" /> <beans:ref bean="sampleInterceptor" /> </interceptor>
<interceptor> //그러니까 아래와 같은 url로 (list.do, insert.do) 접속했을때 loginInterceptor를 적용시키라는 의미. <mapping path="/shop/cart/list.do"/> <mapping path="/shop/cart/insert.do"/> <beans:ref bean="loginInterceptor"/> </interceptor>
<!-- 관리자 세션 체크를 위한 인터셉터 설정 -->
<interceptor> <mapping path="/shop/product/write.do"/> <mapping path="/shop/product/insert.do"/> <beans:ref bean="adminInterceptor" /> </interceptor>
</interceptors> | cs |
인터셉터를 만들어놓으면 컨트롤러마다 세션을 만들지 않아서 되서 편리하다.
3. 관리자 로그인을 체크할 수 있는 인터셉터 만들기
AdminInterceptor.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | package com.example.spring02.interceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
// HandlerInterceptorAdapter 추상클래스 상속 // preHandle(), postHandle() 오버라이딩
public class AdminInterceptor extends HandlerInterceptorAdapter { //로그인 실행전 처리하는 메소드 //세션에 저장되어있는 id가 null이면 로그인 페이지로 이동하고, //세션에 저장되어 있는 id가 있으면 메인 액션으로 이동함
@Override public boolean preHandle(HttpServletRequest request , HttpServletResponse response, Object handler) throws Exception { //세션 객체 생성 HttpSession session=request.getSession(); //세션변수 admin_userid가 없으면 if(session.getAttribute("admin_userid")==null) { //로그인 페이지로 이동 response.sendRedirect(request.getContextPath() +"/admin/login.do?message=nologin"); //메인 액션으로 이동하지 않음 return false; }else { //세션 변수가 있으면 메인 액션으로 이동 return true; } }
//후처리 @Override public void postHandle(HttpServletRequest request , HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { super.postHandle(request, response, handler, modelAndView); } } | cs |
servlet-context.xml 중 일부 (인터셉터 매핑 url 추가, chart랑 jchart도 관리자만 사용할 수 있게끔) | <!-- 관리자 세션 체크를 위한 인터셉터 설정 --> <interceptor> <mapping path="/pdf/list.do"/> <mapping path="/shop/product/write.do"/> <mapping path="/shop/product/insert.do"/> <mapping path="/chart/**" /> <mapping path="/jchart/**" /> <beans:ref bean="adminInterceptor" /> </interceptor> | cs |
프로젝트를 실행하면 관리자 로그인을 한 상태에서만 차트와 j차트를 볼 수 있게끔 하고, 관리자 로그인이 안되어 있으면 관리자 로그인창이 출력된다.
Back-End/Data Base 2019. 6. 27. 12:20
용어
|
설명 |
Table |
데이터 값들의 집합을 뜻한다. |
Row |
Table 내에서 한 줄에 해당하는 데이터 값들. (=Tuple 와 동일한 단어) |
Column |
Table에서 데이터가 갖는 속성값. (=attribute와 동일한 단어)
예시) 상단의 "설명" 이 column에 해당한다.
|
Domain |
도메인은 하나의 Attirbute가 취할 수 있는 같은 타입의 원자 (Atomic)
값들의 집합
|
Attribute |
파일 구조상의 데이터 항목 또는 데이터 필드에 해당된다.
속성 (Attribute)는 개체의 특성을 기술한다.
|
Tuple |
Table (Relation) 을 구성하는 각각의 행을 뜻한다. |
Cardinality |
도메인에 있는 모든 값들의 갯수 |
출처 https://jhkang-tech.tistory.com/6
Back-End/Data Base 2019. 6. 27. 12:14
무결성이란?
무결성이란 데이터베이스에 저장된 데이터 값과 그것이 표현하는 현실 세계의 실제값이 일치하는 정확성을 의미한다.
무결성을 유지하는 방법
대표적으로 사용되는 방법은 중앙 통제에 의한 데이터 갱신으로서, 이 방법은 검증 프로그램을 이용하여 모든 갱신 처리 과정에서
반드시 검증 단계를 거치도록 통제를 가한다.
검증 프로그램이 무결성을 검증하기 위해 무결성 규정을 사용한다.
규정이름 : 무결성 규정을 참조할 때 사용하는 식별자
트리거조건 : 트랜잭션의 접근 유형 및 데이터, 검사할 시기를 명시
제약조건 : 무결성을 위한 검사조건
위반조치 : 검사결과 무결성 위반이 발견되었을 때 처리할 조치
|
-무결성의 종류-
NULL 무결성 : 릴레이션의 특정속성 값이 NULL이 될 수 없도록 하는 규정
고유 무결성 : 릴레이션의 특정 속성에 대해서 각 튜플이 갖는 값들이 서로 달라야 한다는 규정
참조 무결성 : 외래키 값은 NULL 이거나 참조 릴레이션의 기본키 값과 동일해야 한다는 규정 즉 릴레이션은 참조할 수 없는 외래키 값을 가질 수 없다는 규정
도메인 무결성 : 특정 속성의 값이, 그 속성이 정의된 도메인에 속한 값이어야 한다는 규정
키 무결성 : 하나의 테이블에는 적어도 하나의 키가 존재해야 한다는 규정
|
-무결성 제약조건-
데이터베이스에 들어있는 데이터의 정확성 (일관성)을 보장하기 위해 부정확한 자료가 데이터베이스 내에 저장되는 것을 방지하기 위한 제약 조건을 의미함
개체 무결성
릴레이션에서 기본키를 구성하는 속성은 Null값이나 중복값을 가질 수 없다.
참조 무결성
외래키 값은 Null이거나 참조 릴레이션의 기본키 값과 동일해야 한다. 즉, 릴레이션은 참조할 수 없는 외래키 값을 가질 수 없다. 외래키와 참조하려는 테이블의 기본키는 도메인과 속성개수가 같아야 한다.
|
Back-End/API 2019. 6. 27. 11:43
Spring API (@RequestBody와 ResponseEntity의 차이점)
Spring에서는 HttpEntity란 클래스를 제공하는데 이 클래스의 역할은 Http 프로토콜을 이용하는 통신의 header과
body 관련 정보를 저장할 수 있게끔 합니다. 그리고 이를 상속받은 클래스로 RequestEntity와 ResponseEntity가
존재한다.
즉, 통신 메시지 관련 header과 body의 값들을 하나의 객체로 저장하는 것이 HttpEntity 클래스 객체이고,
Request 부분일 경우 HttpEntity를 상속받은 RequestEntity가, Response 부분일 경우 HttpEntity를 상속받은
ResponseEntity가 하게 됩니다.
@ResponseBody나 ResponseEntity를 return 하는거나 결과적으로는 같은 기능이지만.. 그 구현 방법이 틀리다.
예를 들어 header 값을 변경시켜야 할 경우엔 @ResponseBody의 경우 파라미터로 Response 객체를 받아서
이 객체에서 header를 변경시켜야 하고,.. ResponseEntity에서는 이 클래스 객체를 생성한뒤 객체에서
header 값을 변경시키면 된다.
자세한것은 Spring API 문서를 참고하면 된다.
|
ResponseEntity API
| public class ResponseEntity<T> extends HttpEntity<T> { private final Object status; /** * Create a new {@code ResponseEntity} with the given status code, and no body nor headers. * @param status the status code */ | cs |
@RequestBody API
| public @interface RequestBody { /** * Whether body content is required. * <p>Default is {@code true}, leading to an exception thrown in case * there is no body content. Switch this to {@code false} if you prefer * {@code null} to be passed when the body content is {@code null}. * @since 3.2 */ boolean required() default true; } | cs |
Back-End/Spring 2019. 6. 26. 17:52
-AOP 실습예제-
사용자가 메시지를 남기면 포인트 10 증가
메시지를 읽으면 포인트 5 증가
글쓰기를 하면 포인트 10을 부여
글읽기를 하면 열람시간을 수정하고 포인트 5를 부여
뷰는 따로 만들지 않고 크롬의 확장 프로그램을 이용해서 보냄
com.examplt.spring02.aop ㄴMessageAdvice.java
com.example.spring02.controller.message ㄴMessageController.java
com.example.spring02.model.message.dto ㄴUserDTO.java ㄴMessageDTO.java
com.example.spring02.model.message.dao ㄴMessageDAO.java ㄴMessageDAOImpl.java ㄴPointDAO.java ㄴPointDAOImpl.java
com.example.spring02.service.message ㄴMessageService.java ㄴMessageServiceImpl.java
|
AOP 용어 정리
아래 용어들은 Spring에서만 사용되는 용어들이 아닌 AOP 프레임워크 전체에서 사용되는 공용어입니다.
타겟 (Target)
부가기능을 부여할 대상을 의미한다.
여기선 핵심기능을 담당하는 getBoards 혹은 getUsers를 하는 Service들을 의미한다. 애스팩트 (Aspect)
객체지향 모듈을 오브젝트라 부르는 것과 비슷하게 부가기능 모듈을 애스펙트라고 부르며, 핵심기능에 부가되어 의미를 갖는 특별한 모듈이라
생각하면 된다.
애스팩트는 부가될 기능을 정의한 어드바이스와 어드바이스를 어디에 적용할지를 결정하는 포인트컷을 함께 갖고 있습니다.
AOP(Aspect Oriented Programiming) 라는 뜻 자체가 어플리케이션의 핵심적인 기능에서 부가적인 기능을 분리해서 애스팩트라는 모듈로
만들어서 설계하고 개발하는 방법을 의미한다. 어드바이스 (Advice)
실질적으로 부가기능을 담은 구현체를 의미합니다.
어드바이스의 경우 타겟 오브젝트에 종속되지 않기 때문에 순수하게 부가기능에만 집중할 수 있습니다.
어드바이스는 애스펙트가 '무엇' 을 '언제' 할지를 정의하고 있습니다.
포인트컷 (PointCut)
부가기능이 적용될 대상 (메소드) 를 선정하는 방법을 얘기합니다.
즉, 어드바이스를 적용할 조인포인트를 선별하는 기능을 정의한 모듈을 얘기합니다.
조인포인트 (JoinPoint)
어드바이스가 적용될 수 있는 위치를 얘기합니다.
다른 AOP 프레임워크와 달리 Spring에서는 메소드 조인포인트만 제공하고 있습니다.
따라서 Spring 프레임워크 내에서 조인포인트라 하면 메소드를 가리킨다고 생각해도 된다.
프록시 (Proxy)
타겟을 감싸서 타겟의 요청을 대신 받아주는 랩핑(Wrapping) 오브젝트 입니다.
호출자 (클라이언트)에서 타겟을 호출하게 되면 타겟이 아닌 타겟을 감싸고 있는 프록시가 호출되어, 타겟 메소드 실행전에 선처리, 타겟 메소드 실행 후, 후처리를 실행시키도록 구성되어있습니다.
(AOP에서 프록시는 호출을 가로챈 후, 어드바이스에 등록된 기능을 수행 후 타겟 메소드를 호출합니다.)
|
MessageDTO.java (메시지 저장 DTO 클래스) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | .package com.example.spring02.model.message.dto; import java.util.Date; public class MessageDTO { //메시지 저장 DTO 클래스 private int mid; private String targetid; private String sender; private String message; private Date opendate; //java.util.Date private Date senddate; //getter,setter,toString() public int getMid() { return mid; } public void setMid(int mid) { this.mid = mid; } public String getTargetid() { return targetid; } public void setTargetid(String targetid) { this.targetid = targetid; } public String getSender() { return sender; } public void setSender(String sender) { this.sender = sender; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public Date getOpendate() { return opendate; } public void setOpendate(Date opendate) { this.opendate = opendate; } public Date getSenddate() { return senddate; } public void setSenddate(Date senddate) { this.senddate = senddate; } @Override public String toString() { return "MessageDTO [mid=" + mid + ", targetid=" + targetid + ", sender=" + sender + ", message=" + message + ", opendate=" + opendate + ", senddate=" + senddate + "]"; } } | cs |
UserDTO (유저 정보 저장 DTO 클래스) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | .package com.example.spring02.model.message.dto; public class UserDTO { //사용자 정보 저장 DTO 클래스 private String userid; //아이디 private String upw; //비밀번호 private String uname; //이름 private int upoint; //포인트 //getter,setter,toString() public String getUserid() { return userid; } public void setUserid(String userid) { this.userid = userid; } public String getUpw() { return upw; } public void setUpw(String upw) { this.upw = upw; } public String getUname() { return uname; } public void setUname(String uname) { this.uname = uname; } public int getUpoint() { return upoint; } public void setUpoint(int upoint) { this.upoint = upoint; } @Override public String toString() { return "UserDTO [userid=" + userid + ", upw=" + upw + ", uname=" + uname + ", upoint=" + upoint + "]"; } } | cs |
MessageDAO.java | .package com.example.spring02.model.message.dao; import com.example.spring02.model.message.dto.MessageDTO; public interface MessageDAO { //메시지 쓰기 public void create(MessageDTO dto); //메시지 읽기 public MessageDTO readMessage(int mid); //상태 변경 public void updateState(int mid); } | cs |
MessageDAOImpl.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | .package com.example.spring02.model.message.dao; import javax.inject.Inject; import org.apache.ibatis.session.SqlSession; import org.springframework.stereotype.Repository; import com.example.spring02.model.message.dto.MessageDTO; import com.example.spring02.model.shop.dao.CartDAO; @Repository //dao bean으로 등록 public class MessageDAOImpl implements MessageDAO { @Inject //의존관계 주입(Dependency Injection, DI) SqlSession sqlSession; @Override public void create(MessageDTO dto) { //메시지를 추가하는 메소드 sqlSession.insert("message.create", dto); } @Override public MessageDTO readMessage(int mid) { return null; } @Override public void updateState(int mid) { } } | cs |
messageMapper.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와 중복되지 않도록 네임스페이스 기재 --> <mapper namespace="message"> <!--메시지를 만들때 값을 집어넣는 쿼리문 --> <insert id="create"> insert into tbl_message (mid,targetid,sender,message) values ( message_seq.nextval , #{targetid}, #{sender}, #{message} ) </insert> </mapper> | cs |
메시지를 저장한 후에 포인트를 주기위한 Point 관련 인터페이스와 클래스를 만듦
PointDAO.java | .package com.example.spring02.model.message.dao; public interface PointDAO { public void updatePoint(String userid, int point); } | cs |
PointDAOImpl.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | .package com.example.spring02.model.message.dao; import java.util.HashMap; import java.util.Map; import javax.inject.Inject; import org.apache.ibatis.session.SqlSession; import org.springframework.stereotype.Repository; @Repository //스프링에서 관리하게끔 하기 위해 bean 선언 public class PointDAOImpl implements PointDAO { @Inject //의존성을 주입 SqlSession sqlSession; @Override public void updatePoint(String userid, int point) { Map<String,Object> map=new HashMap<>(); //두가지 타입의 값을 넣을때는 HashMap()사용 map.put("userid", userid); //map안에 값들을 저장함 map.put("point", point); //2개 이상의 값을 전달할 경우 - dto, map sqlSession.update("point.updatePoint", map); //네임스페이스와 id를 맞춰서 mapper 쿼리 실행 } } | cs |
pointMapper.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <?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"> <!-- mappers/message/pointMapper.xml --> <!-- 다른 mapper와 중복되지 않도록 네임스페이스 기재 --> <mapper namespace="point"> <!-- sqlSession.update("point.updatePoint", map); --> <update id="updatePoint"> update tbl_user set upoint=upoint+#{point} where userid=#{userid} </update> <!-- 사용자의 아이디에 맞춰서 포인트를 올려주는 쿼리문 --> <!-- 변수 2개가 hashmap<>로 전달되었다. --> </mapper> | cs |
MessageSerivce.java | package com.example.spring02.service.message; import com.example.spring02.model.message.dto.MessageDTO; public interface MessageService { public void addMessage(MessageDTO dto); //메시지를 추가하는 메소드 public MessageDTO readMessage(String userid, int mid); //메시지를 읽는 메소드 } | cs |
-트랜잭션 처리 (거래 처리 단위)-
트랜잭션이 완료되지 않은 상태에서 에러가 발생할 경우 데이터에 오류가 발생함
트랜잭션의 특징중 일관성에 어긋나기 때문.
예를 들어 메시지는 전달되나 포인트는 안올라가는 문제가 발생할 수 있음.
이러한 문제를 예방하기 위해 @Transactional 어노테이션을 트랜잭션 메소드에 사용한다.
이 어노테이션을 사용하면 메소드 안에 있는 코드가 하나라도 에러가 발생할 시 rollback을 시켜버려서 트랜잭션의 일관성을 유지시켜준다.
-사전 설정-
1. 실습하기 전에 root-context.xml 파일의 네임스페이스의 tx (트랜잭션 처리를 할 수 있는 namespaces) 체크를 확인
2. root-context.xml에 트랜잭션 설정 코드가 있는지 확인
| <!-- 트랜잭션 관련 설정 --> //dataSource에서 트랜잭션 관리자 역할을 할수있는 빈을 만든다. //<tx:annotation-driven /> 이 태그는 아까 사용한 @Transactional을 인식할 수 있는 태그이다.
<bean id="transactionManager" //변수명 class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> //자료형 <property name="dataSource" ref="dataSource" /> </bean> //dataSource는 bean을 참조한다. (xml파일의 위쪽에 있음)
<!-- 트랜잭션 관련 어노테이션을 자동 인식하는 옵션 -->
<tx:annotation-driven /> //@Transaction 어노테이션을 인식할 수 있게 하는 태그 | cs |
MessageSerivceImpl.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | .package com.example.spring02.service.message; import javax.inject.Inject; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.example.spring02.model.message.dao.MessageDAO; import com.example.spring02.model.message.dao.PointDAO; import com.example.spring02.model.message.dto.MessageDTO; @Service //현재 클래스를 스프링에서 관리하는 bean으로 설정 public class MessageServiceImpl implements MessageService { //서비스는 dao보다 좀 더 큰 단위의 업무를 수행한다. //dao에서는 세부 단위 업무를 수행 //Inject는 각각 해야 함 //의존성을 주입해야할 DAO클래스가 2개이기때문에 반드시 각각한다. @Inject MessageDAO messageDao; @Inject PointDAO pointDao; //트랜잭션 처리 대상 method //이 어노테이션을 사용하는 메소드는 그 메소드안에 있는 명령이 모두다 실행이 되지 //않으면 rollback을 시켜버린다. //트랜잭션의 일관성을 유지하기 위해. @Transactional //method 내부의 코드를 트랜잭션 (거래처리 단위)로 묶음 @Override public void addMessage(MessageDTO dto) { //메시지를 테이블에 저장 messageDao.create(dto); //메시지를 보낸 회원에게 10포인트 추가 pointDao.updatePoint(dto.getSender(), 10); } @Override public MessageDTO readMessage(String userid, int mid) { // TODO Auto-generated method stub return null; } } | cs |
이 예제를 진행할때는 jsp 페이지를 만들지 않는다.
원래는 만들어야 정상이지만, 이번에는 툴로 처리를 할 것이기 때문에 만들지 않는다.
@RestController을 사용해서 입력되는 데이터와 되돌려주는 데이터를 모두 json으로 처리한다.
MessageController.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | .package com.example.spring02.controller.message; import javax.inject.Inject; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import com.example.spring02.model.message.dto.MessageDTO; import com.example.spring02.service.message.MessageService; @RestController //jsp파일을 사용하지 않고, 입력되는 데이터와 되돌려주는 데이터를 모두 json으로 처리한다. @RequestMapping("messages/*") //공통적인 url mapping public class MessageController { @Inject MessageService messageService; @RequestMapping(value="/", method=RequestMethod.POST) //post방식일때만 이 메소드가 실행되게 만들었다. public ResponseEntity<String> addMessage( @RequestBody MessageDTO dto){ //json형식으로 자료가 입력될때 RequestBody 어노테이션을 사용한다. //RequestBody어노테이션을 DTO를 json형식으로 받는다는 뜻 //즉 json(String)타입으로 들어오면 dto타입으로 변환해서 되돌려준다. ResponseEntity<String> entity=null; try { messageService.addMessage(dto); entity=new ResponseEntity<>("success",HttpStatus.OK); //ResponseEntiry<>는 메시지 (성공 or 실패)와 에러코드를 함께 출력하고 싶을때 사용하는 타입 } catch (Exception e) { //메시지 처리가 실패하면 실행되는 코드, 메시지와 에러코드를 출력 e.printStackTrace(); entity=new ResponseEntity<>(e.getMessage() ,HttpStatus.BAD_REQUEST); //메시지 처리가 실패하면 메시지와 400에러를 출력시킨다. } return entity; //메시지와 에러코드를 같이 리턴한다. 그리고 되돌려주는 값도 json형식 으로 보낸다. } } | cs |
서비스 쪽에 AOP 설정을 해서 전, 후에 처리가 어떻게 되는지 확인
@Bean과 @Component의 차이
@Bean의 경우 개발자가 컨트롤이 불가능한 외부 라이브러리들을 Bean으로 등록하고 싶은 경우에 사용된다.
( 예를 들면 ObjectMapper의 경우 ObjectMapper Class에 @Component를 선언할 수는 없으니 ObjectMapper의 인스턴스를 생성하는 메소드를
만들고 해당 메소드에 @Bean을 선언하여 Bean으로 등록한다.)
반대로 개발자가 직접 컨트롤이 가능한 Class들의 경우엔 @Component를 사용한다.
|
MessageAdvice.java
@Before 어노테이션으로 MessageService.java 시작 전에는 로그 수집을 하고,
@Around 어노테이션으로 MessageService.java 시작 전, 후에 실행시간을 체크한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | package com.example.spring02.aop; import java.util.Arrays; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; @Component // 기타 bean, 개발자가 직접 컨트롤이 가능한 class의 경우에 사용하는 어노테이션 @Aspect // aop bean - 공통 업무를 지원하는 코드 public class MessageAdvice { private static final Logger logger = LoggerFactory.getLogger(MessageAdvice.class); //MessageAdvice.class의 로그를 출력 @Before( //핵심업무 수행전에 실행이 되는 어노테이션 "execution(* " //================================================================ + " com.example.spring02.service.message" + ".MessageService*.*(..))") // MessageSerivce로 시작하는 모든 클래스의 모든 메소드들, 모든파라미터들에 대해서 // execution구문이 실행되기 전에 startLog메소드가 먼저 실행된다. //================================================================ //그러니까 메시지 서비스 시작하기 전에는 로그수집을 한다. public void startLog(JoinPoint jp) { //execution구문이 실행되기 바로 전에 호출됨 //joinPoint는 advice를 적용 가능한 지점을 의미한다. //메소드 호출, 필드 값 변경 등이 Joinpoint에 해당한다. logger.info("핵심 업무 코드의 정보:" +jp.getSignature()); //시그니처의 값을 반환 logger.info("method:" +jp.getSignature().getName()); //시그니처의 이름을 반환 logger.info("매개변수:" +Arrays.toString(jp.getArgs())); //클라이언트가 메소드를 호출할 때 넘겨준 인자 목록을 Object 배열로 리턴함 } @Around( //호출 전 후에 실행 되게끔하는 어노테이션 "execution(* " + " com.example.spring02.service.message" + ".MessageService*.*(..) )") public Object timeLog(ProceedingJoinPoint pjp) throws Throwable {//MessageService 시작 전후로는 시간체크를 한다. //호출 전(Before) long start=System.currentTimeMillis(); //long타입의 값이며, 1/1000초의 값을 리턴한다. 정확한 시간을 측정할때 사용 //========================================================= Object result=pjp.proceed(); //이 구문을 기준으로 위쪽은 호출 전이고, 아래쪽은 호출 후 이다. //========================================================= //호출 후(After) long end=System.currentTimeMillis(); //long타입의 값이며, 1/1000초의 값을 리턴한다. 정확한 시간을 측정할때 사용 logger.info(pjp.getSignature().getName()+":"+(end-start)); //시그니처의 이름과 실제 실행한 시간을 로그로 찍는다. logger.info("================="); // return result; } } | cs |
(실습 방법)
이번 실습에서는 뷰 (view)를 별도로 만들지 않고 Advanced REST client 확장 프로그램으로만 테스트를 수행합니다.
method : POST
Request URL : http://localhost/spring02/messages/
Body > Body content type > application/json
Editor view > Raw input
Advanced REST client 확장 프로그램 설치
(파라미터를 사용해서 컨트롤러로 값을 보낼 수 있음)
구글 검색창에 Advanced REST client 검색
|
일단 입력 데이터를 json으로 보내기 위해 사용해보기
put : 전체 수정
delete : 삭제
patch : 일부분 수정
Method : POST
Request URL : http://localhost/spring02/message/Body
Body content type : application/json
Editor view : Raw input
다음 처럼 설정하고 json 코드를 작성
json 코드 작성하고 send버튼을 누른다.
| { "targetid" : "user01", //"변수" : "값" 형식이다, 즉, "targetid" 변수의 값은 "user01" 이다. "sender" : "user02", "message" : "Good morning" } // 해석 : user02가 user01에게 "Good morning" 메시지를 보낸다는 뜻. | cs |
하단에 200 OK버튼이 뜨면 정상적으로 처리된 것이다.
MessageController.java (메시지 컨트롤러) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | package com.example.spring02.controller.message; import javax.inject.Inject; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import com.example.spring02.model.message.dto.MessageDTO; import com.example.spring02.service.message.MessageService; @RestController //json을 리턴하는 method가 있는 경우에는 RestController을 사용 @RequestMapping("messages/*") //공통적인 url mapping //메시지를 추가할때 맵핑되는 컨트롤러 public class MessageController { @Inject MessageService messageService; //서비스를 호출하기위해 의존성을 주입한다. @RequestMapping(value="/", method=RequestMethod.POST) //POST방식의 메소드와 맵핑 public ResponseEntity<String> addMessage( // 크롬 ARC를 사용해서 데이터를 보냈는데 String타입이기때문에 dto에 저장하려면 // 변환과정이 필요하다. // 그 변환과정을 RequestBody라는 어노테이션을 붙이면서 자동적으로 dto타입으로 변환이 되었다.
@RequestBody와 ResponseEntity의 차이점
Spring에서는 HttpEntity란 클래스를 제공하는데 이 클래스의 역할은 Http 프로토콜을 이용하는 통신의 header과
body 관련 정보를 저장할 수 있게끔 합니다. 그리고 이를 상속받은 클래스로 RequestEntity와 ResponseEntity가
존재한다.
즉, 통신 메시지 관련 header과 body의 값들을 하나의 객체로 저장하는 것이 HttpEntity 클래스 객체이고,
Request 부분일 경우 HttpEntity를 상속받은 RequestEntity가, Response 부분일 경우 HttpEntity를 상속받은
ResponseEntity가 하게 됩니다.
@ResponseBody나 ResponseEntity를 return 하는거나 결과적으로는 같은 기능이지만.. 그 구현 방법이 틀리다.
예를 들어 header 값을 변경시켜야 할 경우엔 @ResponseBody의 경우 파라미터로 Response 객체를 받아서
이 객체에서 header를 변경시켜야 하고,.. ResponseEntity에서는 이 클래스 객체를 생성한뒤 객체에서
header 값을 변경시키면 된다.
자세한것은 Spring API 문서를 참고하면 된다. @RequestBody MessageDTO dto){ ResponseEntity<String> entity=null; try { messageService.addMessage(dto); entity=new ResponseEntity<>("success",HttpStatus.OK); //되돌려줄 메시지는 success, 상태메시지는 상태코드는 200이다. } catch (Exception e) { e.printStackTrace(); entity=new ResponseEntity<>(e.getMessage() ,HttpStatus.BAD_REQUEST); } return entity; } } | cs |
데이터베이스에서 데이터를 삭제해보고 크롬브라우저에서 send를 사용해서 컨트롤러로 자료를 보내서 테스트를 해본다.
로그를 확인해보면 dto에 값들이 쌓여있는것을 확인할 수 있다.
Back-End/Spring 2019. 6. 26. 17:11
HTTP 응답 코드의 종류
응답 코드
|
설 명 |
100 |
Continue (클라이언트로부터 일부 요청을 받았으며 나머지 정보를 계속 요청함) |
101 |
Switching protocols |
200 |
OK (요청이 성공적으로 수행되었음) |
201 |
Created (PUT 메소드에 의해 원격지 서버에 파일이 생성됨) |
202 |
Accepted (웹 서버가 명령을 수신함) |
203 |
Non - authoritative information (서버가 클라이언트 요구 중 일부만 전송) |
204 |
No content, (사용자 요구를 처리하였으나 전송할 데이터가 없음) |
301 |
Moved permanently (요구한 데이터를 변경된 타 URL에 요청함) |
302 |
Not temporarily |
304 | Not modified (컴퓨터 로컬의 캐시 정보를 이용함. 대개 gif 등은 웹 서버에 요청하지 않음) | 400 | Bad request (사용자의 잘못된 요청을 처리할 수 없음) , 스프링으로 작업할 때 많이 나는 에러 | 401 | Unauthorized (인증이 필요한 페이지를 요청한 경우) | 402 | Payment required (예약됨) | 403 | Forbidden (접근 금지, 디렉터리 리스팅 요청 및 관리자 페이지 접근 등을 차단) | 404 | Not found. (요청한 페이지가 없음) | 405 | Method not allowed (허용되지 않는 http method 사용함) | 407 | Proxy authentication required (프록시 인증 요구됨) | 408 | Request timeout (요청 시간 초과) | 410 | Gone (영구적으로 사용 금지) | 412 | Precondition failed (전체 조건 실패) | 500 | Internal server error (내부 서버 오류) | 501 | Not implemented (웹 서버가 처리할 수 없음) | 503 | Service unnailable (서비스 제공 불가) | 504 | Gateway timeout (게이트웨이 시간 초과) | 505 | HTTP version not supported (해당 http 버전이 지원되지 않음) |
HTTP 메소드의 종류
HTTP Method
|
전송 형태 |
설명 |
GET |
GET [request-uri]?query_string
HTTP/1.1\r\n
Host:[Hostname] 혹은 [IP] \r\n
|
GET 요청 방식은 URI (URL) 이 가진 정보를 검색하기 위해 서버측에 요청하는 형태이다. |
POST |
POST [request-uri]?query_string
HTTP/1.1\r\n
HOST:[Hostname] 혹은 [IP] \r\n
Content-Lenght:[Lenght in Bytes] \r\n
\r\n
[query-string] 혹은 [데이터]
|
POST 요청 방식은 요청 URI (URL)에 폼 입력을
처리하기 위해 구성한 서버 측 스크립트
(ASP, PHP, JSP 등) 혹은 CGI 프로그램으로
구성되고 Form Action과 함께 전송되는데,
이 때 헤더 정보에 포함되지 않고 데이터 부분에
요청 정보가 들어가게 된다. |
HEAD | HEAD [request-uri]
HTTP/1.1\r\n
Host:[Hostname] 혹은 [IP] \r\n |
HEAD 요청 방식은 GET과 유사한 방식이나
웹 서버에서 헤더 정보 이외에는 어떤 데이터도
보내지 않는다.
웹 서버의 다운 여부 점검 (Health Check)이나
웹 서버 정보 (버전 등) 등을 얻기 위해
사용될 수 있다.
| OPTIONS | OPTIONS [request-uri]
HTTP/1.1\r\n
Host:[Hostname] 혹은 [IP] \r\n
|
해당 메소드를 통해 시스템에서 지원되는
메소드의 종류를 확인할 수 있다. | PUT | PUT [request-uri] HTTP/1.1\r\n
Host:[Hostname] 혹은 [IP] \r\n
Content-Lenght:[Length]
| POST와 유사한 전송 구조를 가지기 때문에 헤더 이외에 메시지 (데이터)가 함께 전송된다.
원격지 서버에 지정한 콘텐츠를 저장하기 위해
사용되며 홈페이지 변조에 많이 악용되고 있다.
| DELETE | DELETE [request-uri]
HTTP/1.1 \r\n
Host:[Hostname] 혹은 [IP] \r\n \r\n
| 원격지 웹 서버에 파일을 삭제하기 위해 사용되며 PUT과는 반대 개념의 메소드이다.
| TRACE | TRACE [request-uri]
HTTP/1.1 \r\n
Host:[Hostname] 혹은 [IP] \r\n \r\n
| 원격지 서버에 Loopback (루프백) 메시지를 호출하기 위해 사용된다.
| CONNECT | CONNECT [request-uri]
HTTP/1.1 \r\n
Host:[Hostname] 혹은 [IP] \r\n \r\n
| 웹 서버에 프락시 기능을 요청할 때 사용된다. |
Back-End/Spring 2019. 6. 24. 23:17
AOP 실습예제
사용자가 메시지를 남기면 포인트 10 증가 메시지를 읽으면 포인트 5 증가
com.examplt.spring02.aop ㄴMessageAdvice.java
com.example.spring02.controller.message ㄴMessageController.java
com.example.spring02.model.message.dto ㄴUserDTO.java ㄴMessageDTO.java
com.example.spring02.model.message.dao ㄴMessageDAO.java ㄴMessageDAOImpl.java ㄴPointDAO.java ㄴPointDAOImpl.java
com.example.spring02.service.message ㄴMessageService.java ㄴMessageServiceImpl.java
|
-AOP 실습용 테이블을 생성-
(데이터베이스를 이용한 실습)
AOP와 트랜잭션 처리를 활용한 실습
글쓰기를 하면 포인트 10을 부여 글읽기를 하면 열람시간을 수정하고 포인트 5를 부여
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 | drop table tbl_user cascade constraints //혹시 중복된 테이블이 있을수도 있기 때문에 테이블 삭제 create table tbl_user ( //tbl_user 테이블을 생성 userid varchar2(50) not null, //id upw varchar2(50) not null, //비밀번호 uname varchar2(100) not null, //이름 upoint number default 0, //포인트, 기본값은 0으로 한다 primary key(userid) //기본키는 id로 한다. ); create table tbl_message( //tbl_message (메시지를 저장) 테이블을 생성 mid number not null, //메시지의 id targerid varchar2(50) not null, //메시지를 받을 사람의 id sender varchar2(50) not null, //메시지를 보낸 사람의 id message varchar2(4000) not null,//메시지의 내용 opendate date, //메시지를 열어본 시간 senddate date default sysdate, //메시지를 보낸 시간 (보낼 당시 현재 시간) primary key(mid) //기본키를 메시지의 id로 설정 ); --시퀀스 생성-- create sequence message_seq //message_seq 시퀀스를 생성 (메시지가 하나 생길때마다 숫자가 1씩증가, 초기값은 1로 설정) start with 1, increment by 1; --제약 조건 설정-- alter table tbl_message add constraint fk_usertarger //tbl_message테이블에 제약조건을 설정 foreign key (targetid) references tbl_user(userid); //무결성이 깨질수 있기 때문에 targetid값에 userid값만 들어갈 수 있도록 설정 (아무id나 들어가면 안되기 때문에) alter table tbl_message add constraint fk_usersender //tbl_message테이블에 제약조건을 설정 foreign key (sender) references tbl_user(userid); //위쪽과 마찬가지로 sender값에 userid값만 들어갈 수 있도록 설정함 --사용자 추가-- insert into tbl_user (userid, upw, uname) values ('user00','user00','kim'); insert into tbl_user (userid, upw, uname) values ('user01','user01','park'); insert into tbl_user (userid, upw, uname) values ('user02','user02','hong'); insert into tbl_user(userid, upw, uname) values ('user03','user03','choi'); insert into tbl_user(userid, upw, uname) values ('user04','user04','lee'); select * from tbl_user; --user02가 user00에게 메시지를 전송 insert into tbl_message (mid, targetid, sender, message) values (message_seq.nextval, 'user00','user02','안녕...'); --user02에게 포인트 10 추가 update tbl_user set upoint=upoint+10 where userid='user02'; select * from tbl_user; --user00의 메시지박스 조회 select * from tbl_message where targetid='user00'; update tbl_message set opendate=sysdate where mid=2; //메시지를 읽으면 읽은 시간을 현재시간으로 바꾼다. select * from tbl_message; update tbl_user set upoint=upoint+5 where userid='user00'; //userid가 user00과 같으면 포인트를 5포인트 증가시킨다. select * from tbl_user; delete from tbl_message; update tbl_user set upoint=0; //tbl_message테이블이 삭제될때 tbl_user테이블의 포인트를 0으로 한다. --메시지 일련번호를 관리할 시퀀스 객체 create sequence message_seq start with 1 --1부터 시작 increment by 1; --1씩 증가 --시퀀스.nextval => 다음번호 select message_seq.nextval from dual; //이 구문을 실행시키게 되면 마치 은행에서 번호표를 뽑듯이 번호가 1씩 계속 증가가된다. //또한 이미 내가 뽑은 번호의 앞번호로 돌아갈수는 없고, 내가 뽑은 번호가 기억이 된다. ====================포인트를 얻게하는 테스트========================================================================
--테스트를 위한 사용자 계정을 추가한다. insert into tbl_user (userid, upw, uname) values ('user00','user00','kim'); //아이디, 비밀번호, 이름 insert into tbl_user (userid, upw, uname) values ('user01','user01','park'); insert into tbl_user (userid, upw, uname) values ('user02','user02','hong'); insert into tbl_user (userid, upw, uname) values ('user03','user03','choi'); insert into tbl_user (userid, upw, uname) values ('user04','user04','lee'); select * from tbl_user; --메시지 보내기 테스트-- --user02가 user00에게 메시지 전송 insert into tbl_message (mid, targetid, sender, message) values (message_seq.nextval,'user00','user02','안녕'); //해석 : user02가 user00에게 '안녕'이라는 메시지를 보낸다. select * from tbl_message; --user02에게 포인트 10 추가 update tbl_user set upoint=upoint+10 where userid='user02'; select * from tbl_user; --user00이 메시지 목록 select * from tbl_message where targetid='user00'; --메시지를 읽으면 열람시간을 저장 update tbl_message set opendate=sysdate where mid=18; //18번 메시지를 읽으면 읽은 열람시간을 현재시간으로 바꾸기 select * from tbl_message; --메시지를 읽으면 읽은 사람한테 5포인트를 추가 update tbl_user set upoint=upoint+5 where userid='user00'; select * from tbl_user;
| cs |
테스트 종료 후 테이블 삭제 및 commit 실시
| delete from tbl_message; //메시지 테이블은 지금 필요 없기 때문에 일단 삭제 commit; //지금까지 한 작업들을 반영하기 위해 commit를 | cs |
|