반응형

**기본키 중복 검사

=>기본키를 직접 입력받는 경우에는 중복검사를 해주는 것이 좋습니다.

이벤트는 포커스가 잃어버릴 때를 많이 이용하고 이 때 ajax websocket을 이용해서 서버에게 기본키 입력 값을 전송하고 서버는 넘어온 기본키 입력 값을 가지고 데이터베이스에서 조회해서 그 결과를 json 이나 xml 형태로 클라이언트에게 알려주며 클라이언트는 그 값을 가지고 중복 여부를 판단해서 처리를 합니다.

 

=>기본키를 일련번호 형태로 자동 생성하는 경우는 auto_increment sequence를 이용하기도 하고 현재 입력된 번호 중 가장 큰 번호를 찾고 여기에 +1을 해서 생성하는 방법도 있습니다.

이 방법은 일반적으로 비추천하고 되도록이면 숫자나 문자의 조합을 이용해서 분류의 기능을 갖도록 만드는 것을 권장합니다.

대표적으로 주민번호나 학번

 

**폼의 데이터 전송 시 유효성 검사

=>클라이언트에서의 유효성 검사

클라이언트에서 유효성 검사를 하게되면 서버로 전송하지 않고 수행을 하기 때문에 속도나 트래픽 면에서 유리하지만 보안이 되지 않기 때문에 클라이언트가 유효성 검사 로직을 파악할 수 있습니다.

=>폼의 경우는 submit 이벤트에서 유효성검사하고 유효성 검사를 통과하지 못하면 서버로 전송하지 못하도록 이벤트객체의 preventDefault()를 호출하면 됩니다.

=>가장 많이 수행하는 유효성 검사는 필수 입력, 중복 검사 통과 여부, 패턴 일치, 2개의 값일치 등이 있습니다.

 

실습:insert.jsp 파일에 유효성 검사를 위한 스크립트 코드를 추가

          

                       //폼의 데이터를 전송할 때 발생하는 이벤트 처리

           document.getElementById("myform").addEventListener("submit",function(e){

                                  //중복 체크 통과여부 확인

                                  if(idcheck.value == false){

                                              iddiv.innerHTML = '아이디 중복검사를 하세요';

                                              iddiv.style.color = 'red';

                                              itemid.focus();

                                              //폼의 데이터를 전송하지 않도록 하기

                                              e.preventDefault();

                                             return;

                                  }

                                 

                                  var price = document.getElementById("price");

                                  //price 입력 란의 숫자만 입력되었는지 체크

                                  //+ - 기호를 앞에 붙일 수 있는지

                                  //,의 경우는 어떻게 할것인지

                                  for(var i=0; i<price.value.length; i=i+1){

                                              var ch = price.value.charAt(i);

                                              if(i == 0){

                                                         if(!(ch == '+' || ch == '-' || (ch >= '0' && ch <= '9'))){

                                                                     price.focus();

                                                                     alert("가격의 첫번째 자리는 숫자나 +, - 기호여야 합니다.")

                                                                     //폼의 데이터를 전송하지 않도록 하기

                                                                     e.preventDefault();

                                                                     return;

                                                         }

                                              }else{

                                                         if(!(ch >= '0' && ch <= '9')){

                                                                     price.focus();

                                                                     alert("가격은 숫자로만 입력하세요!!!")

                                                                     //폼의 데이터를 전송하지 않도록 하기

                                                                     e.preventDefault();

                                                                     return;

                                                         }

                                              }

                                  }

                       });

 

**Spring에서의 File Upload 처리

=>MultipartFile 타입으로 처리

1.준비사항

1)commons-fileupload 라이브러리의 의존성 설정

2)CommonsMultipartResolver 클래스의 bean을 생성

 

2.MultipartFile

=>파라미터를 직접 이 타입으로 받아도 되고 HttpServletRequest 대신에 MultipartHttpServletRequest를 이용해서 요청을 받고 getFile(String parameterName)을 이용해서 가져올 수 있습니다.

 

3.실습

1)파일 업로드 처리를 위한 라이브러리의 의존성을 pom.xml 파일에 추가

<!-- 파일 업로드 처리를 위한 의존성 라이브러리 -->

<dependency>

           <groupId>commons-fileupload</groupId>

           <artifactId>commons-fileupload</artifactId>

           <version>1.3.1</version>

</dependency>

 

2)파일 업로드를 처리해 줄 수 있는 CommonsMultipartFile 클래스의 bean을 추가

=>servlet-context.xml 파일에 추가

<!-- 파일 업로드를 처리할 Bean(spring이 생성하고 관리하는 Instance)을 생성 -->

<beans:bean class="org.springframework.web.multipart.commons.CommonsMultipartResolver"

           id = "multipartResolver">

</beans:bean>

 

3)DAO 클래스에 데이터를 삽입하기 위한 메소드를 생성하고 추가

//데이터 1개를 삽입하는 메소드

public int insertItem(Item item) {

           sessionFactory.getCurrentSession().save(item);

           return 1;

}

 

4)ItemService 인터페이스에 데이터를 삽입하기 위한 메소드를 선언

//데이터 삽입(파일 업로드) 처리를 위한 메소드

public int insertItem(MultipartHttpServletRequest request);

 

5)ItemServiceImpl 클래스에 데이터를 삽입하기 위한 메소드를 구현

           @Override

           @Transactional

           public int insertItem(MultipartHttpServletRequest request) {

                       //파라미터 읽기

                       String itemid = request.getParameter("itemid");

                      String itemname = request.getParameter("itemname");

                       String price = request.getParameter("price");

                       String description = request.getParameter("description");

                      

                       //Dao 객체의 파라미터 만들기

                       Item item = new Item();

                       item.setItemid(Integer.parseInt(itemid));

                       item.setItemname(itemname);

                       item.setPrice(Integer.parseInt(price));

                       item.setDescription(description);

                      

                       //파일 읽기

                       MultipartFile mf = request.getFile("pictureurl");

                       //업로드할 파일이 있는 경우에만

                       if(mf.isEmpty() == false) {

                                  //원본 파일이름 가져오기

                                  String originName =

                                             request.getFile("pictureurl").getOriginalFilename();

                                  //원본 파일 이름은 여러 개의 파일을 업로드 하다보면 중복될 수 있기 때문에

                                  //파일 이름을 만들 때는 동일한 디렉토리에 저장한다면 중복 되지 않도록

                                  //파일 이름을 생성할 필요가 있습니다.

                                  //기본키와 파일명을 합치는 방법이 있고 UUID 클래스를 이용해서 만드는 방법

                                  String uploadName = itemid + originName;

                                  item.setPictureurl(uploadName);

                                 

                                  //파일을 저장할 경로를 생성

                                  //프로젝트의 내의 경로를 가지고 절대경로를 생성

                                  //프로젝트 내의 경로가 아니면 직접 경로를 작성

                                  String uploadPath = request.getRealPath("/img");

                                  //Servlet 3.0 이상인 경우는

                                  //request.getServletContext().getRealPath("/img")

                                 

                                  //업로드할 File 객체 생성

                                  File file = new File(uploadPath + "\\" + uploadName);

                                  try {

                                             request.getFile("pictureurl").transferTo(file);

                                  } catch (IllegalStateException e) {

                                              // TODO Auto-generated catch block

                                              e.printStackTrace();

                                  } catch (IOException e) {

                                              // TODO Auto-generated catch block

                                              e.printStackTrace();

                                  }

                       }

                      

                       //데이터 삽입

                       return hibernateDao.insertItem(item);

           }

 

6)HomeController 클래스에 insert post 방식으로 요청했을 때 처리를 위한 메소드를 생성

@RequestMapping(value="/insert", method=RequestMethod.POST)

           public String insert(

                                  MultipartHttpServletRequest request,

                                  Model model) {

                       //서비스의 메소드를 호출

                       itemService.insertItem(request);

                      

                       //삽입, 삭제, 갱신 등 새로고침을 했을 때 이전 작업을 다시 수행하면 안되는 경우에는

                       //리다이렉트를 해야 합니다.

                       //리다이렉트를 할 때는 View 이름이 아니고 URL을 작성

                       return "redirect:./";

           }

 

 

7)실행하고 파일을 업로드 했을 때 파일을 확인해야 하는 디렉토리

=>자신의워크스페이스\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\wtpwebapps\프로젝트이름

 

=>프로젝트는 원래 모양 그대로 있고 실행이 될 때는 프로젝트의 내용을 build 해서 위의 디렉토리에 있는 내용이 실행되는 것입니다.

 

=>프로젝트를 복사해서 다른 곳에서 실행하는 경우 프로젝트를 만들 때 존재했던 파일들은 그대로 있지만 실행 중 생성한 파일들은 없습니다.

개발환경에서 운영환경으로 이전(이 작업을 이행이라고 합니다.)할 때 기존 데이터베이스 내용을 전부 삭제해야 할 때도 있습니다.

 

**WebSocket

=>http https는 연결형 프로토콜이기는 하지만 한 번 요청을 해서 응답을 받으면 연결이 끊어집니다.

=>채팅과 같은 애플리케이션을 만들 때 http https를 이용하는 것은 어렵습니다.

데이터를 여러번 주고 받고자 하는 경우 연결하고 끊고 다시 연결하고 끊고 하는 작업을 반복해야 합니다.

데이터를 주고받는 시간보다 연결과 해제에 많은 자원을 소모합니다.

그래서 등장방법 중의 하나는 애플리케이션을 설치하는 방법이었습니다.

=>순수 웹 환경에서 실시간 양방향 통신을 위한 Spec HTML5에서 추가가 되었는데 이 Spec WebSocket 입니다.

WebSocket은 한 번 연결하면 close 할 때 까지 계속 연결되어 있습니다.

 

=>최근에는 Android iOS에서도 웹 소켓에 접속하는 API가 추가

 

1.클라이언트(웹 브라우저)에서의 사용 방법(자바스크립트)

1)WebSocket 객체 생성

new WebSocket("ws://domain/demo")

 

2)데이터 전송

WebSocket객체.send("데이터")

 

3)데이터 전송받기 - 콜백 사용

WebSocket객체.addEventListener("message", function(e)){

           //매개변수 e가 전송된 데이터입니다.

}

 

4)기타 이벤트

open, close 이벤트

 

5)연결 해제

WebSocket객체.close()

 

2.Spring을 이용한 WebSocket 서버 설정

=>WebSocketHandler 인터페이스를 구현한 클래스를 생성하고 메소드 재정의

 

=>클래스 위에 @EnableWebSocket 이나 설정 파일에 websocket:handlers 태그를 이용해서 웹 소켓 서버 설정

 

3.WebSocketHandler 인터페이스를 구현한 클래스

=>TextWebSocketHandler, AbstractWebSocketHandler

 

4.의존성 라이브러리

spring-websocket

 

5.주의할 점

=>spring websocket은 서블릿 3.0부터 사용 가능

 

6.실습

1)pom.xml 파일에 spring-websocket 의존성 설정

<!-- 웹 소켓 사용을 위한 의존성 라이브러리 -->

<dependency>

           <groupId>org.springframework</groupId>

           <artifactId>spring-websocket</artifactId>

           <version>${org.springframework-version}</version>

</dependency>

 

2)WebSocket 서버 클래스를 생성

=>TextWebSocketHandler 클래스를 상속

=>기본 패키지 안에 생성

=>com.pk.db.util.ChatHandler

 

//Bean 생성을 자동으로 해주기 위한 어노테이션

@Component

//웹 소켓 채팅 서버 클래스

public class ChatHandler extends TextWebSocketHandler {

           //접속한 유저 목록을 가질 List를 생성

           private static List<WebSocketSession> users

                       = new ArrayList<WebSocketSession>();

          

           //클라이언트가 접속했을 때 호출될 메소드

           //매개변수로 대입된 데이터가 접속한 클라이언트

           @Override

           public void afterConnectionEstablished(WebSocketSession session) {

                       //List에 추가

                       users.add(session);

           }

          

           //클라이언트가 접속을 해제했을 때 호출될 메소드

           //매개변수로 대입된 데이터가 접속을 해제한 클라이언트

           @Override

           public void afterConnectionClosed(

                       WebSocketSession session, CloseStatus status) {

                       //List에서 제거

                       users.remove(session);

           }

          

           //메시지가 전송되었을 때 호출되는 메소드

           @Override

           public void handleTextMessage(

                       WebSocketSession session, TextMessage message) {

                       //전송된 메시지를 출력

                       System.out.println(message.getPayload() + "가 전송됨");

                       //전송받은 메시지를 모든 유저에게 다시 전송

                       for(WebSocketSession ses : users) {

                                  try {

                                              ses.sendMessage(new TextMessage(message.getPayload()));

                                  } catch (IOException e) {

                                              e.printStackTrace();

                                  }

                       }

           }

          

}

 

3)ChatHandler 클래스를 URL과 매핑하는 코드를 servlet-context.xml 파일에 작성

=>servlet-context.xml 파일에 websocket 네임스페이스를 추가

 

<!-- WebSocket 클래스와 URL 매핑 -->

<websocket:handlers>

<!-- handler에서 bean id를 작성하고 path는 클라이언트가 접속할 URL -->

           <websocket:mapping handler="chatHandler" path="/chat-ws"/>

</websocket:handlers>

 

4)home.jsp 파일에 채팅 페이지로 이동할 링크를 추가

<a href="chat">WebSocket을 이용한 채팅 구현</a><br/>

 

5)HomeController 클래스에 위의 요청을 처리하는 메소드를 작성

//chat 이라는 요청이 오면 chat이라는 문자열을 가지고 ViewResolver 설정을 확인해서

//뷰 페이지를 결정 - WEB-INF/views/?.jsp

@RequestMapping(value="/chat", method=RequestMethod.GET)

public String chat(HttpServletRequest request, Model model) {

           //뷰이름을 리턴

           return "chat";

}

 

6)views 디렉토리에 chat.jsp 파일을 만들고 작성

<%@ page language="java" contentType="text/html; charset=UTF-8"

    pageEncoding="UTF-8"%>

<!DOCTYPE html>

<html>

<head>

<meta charset="UTF-8">

<title>채팅</title>

<style>

           #chatArea{

                       width:200px;

                       height:100px;

                       overflow-y:auto;

                       border:1px solid black;

           }

</style>

</head>

<body>

           <a href="./">메인화면으로 이동</a><br />

           이름<input type="text" id="nickname"/>

           <input type="button" id="enterbtn" value="입장"/>

           <input type="button" id="exitbtn" value="나가기"/>

          

           <h1>채팅 창</h1>

           <div id="chatArea">

                       <div id="chatmessagearea"></div>

           </div>

           <br />

          

           <input type="text" id="message"/>

           <input type="button" id="sendbtn" value="전송"/>

          

           <script>

                       //웹 소켓 변수

                       var wsocket

                      

                       //변수에 이름을 기재하고 함수를 대입하면

                       //이 함수를 만들고 난 후 뒤에서 사용가능해집니다.

                       //function 이름(매개변수){}로 만들면 순서에 상관없이 아무곳에서나 호출 가능

                      

                       //문자열을 출력하는 함수

                       var appendMessage = function(msg){

                                  document.getElementById("chatmessagearea").innerHTML =

                                              msg + "<br/>" +

                                             document.getElementById("chatmessagearea").innerHTML

                       }

                      

                       //이벤트 처리 함수

                       var onOpen = function(){

                                  appendMessage("연결 되었습니다.");

                       }

                       var onClose = function(){

                                  appendMessage("연결 해제되었습니다.");

                                  wsocket.close();

                       }

                       var onMessage = function(evt){

                                  var data = evt.data;

                                  appendMessage(data);

                       }

                       var send = function(){

                                  //입력한 내용을 WebSocket 서버에게 전달하고 message 란은 클리어

                                  var nickname = document.getElementById("nickname").value;

                                  var msg = document.getElementById("message").value;

                                  wsocket.send(nickname + ":" + msg);

                                  document.getElementById("message").value = '';

                       }

                      

                       //웹 소켓 연결함수

                       var connect = function(){

                                  //http://localhost:9000/db/chat-ws - 자기 컴퓨터에서만 접속

                                  wsocket = new WebSocket("ws://localhost:9000/db/chat-ws")

                                  //이벤트 핸들러 연결

                                  wsocket.addEventListener("open", onOpen);

                                  wsocket.addEventListener("message", onMessage);

                       };

                      

                       //message 입력란에서 키보드 이벤트가 발생하면

               document.getElementById("message").addEventListener("keypress",

                                              function(e){

                                  //enter를 누르면 send() 호출

                                  event = e || window.event;

                                  var keycode = (event.keyCode?event.keyCode:event.which);

                                  if(keycode == 13){

                                              send()

                                  }

                                  event.stopPropagation();

                       })

                      

                       //버튼들의 이벤트 처리

                      document.getElementById('sendbtn').addEventListener("click", function(e){

                                  send();

                       })

                      document.getElementById('enterbtn').addEventListener("click", function(e){

                                  connect();

                       })

                      document.getElementById('exitbtn').addEventListener("click", function(e){

                                  onClose();

                       })

          

           </script>

</body>

</html>

 

7)pom.xml 파일의 서블릿 설정을 2.5에서 3.1.0으로 수정

<!-- Servlet -->

<dependency>

           <groupId>javax.servlet</groupId>

           <artifactId>javax.servlet-api</artifactId>

           <version>3.1.0</version>

</dependency>

 

**로그인 기능 추가

1.데이터베이스에 접속해서 로그인 처리를 테이블을 생성하고 샘플 데이터를 입력

CREATE TABLE  member(

           num number(10),

           userid varchar2(100) UNIQUE,

           userpw varchar2(100) NOT NULL,

           nickname varchar2(100),

           PRIMARY KEY (num)

);

 

INSERT INTO MEMBER values(1, 'root', '1234', '관리자');

INSERT INTO MEMBER values(2, 'hhh', '1234', '');

INSERT INTO MEMBER values(3, 'jessica', '1234', '제시카');

 

COMMIT;

 

SELECT * FROM MEMBER;

 

2.테이블과 매핑할 DTO 클래스를 생성

=>com.pk.db.domain.Member

 

package com.pk.db.domain;

 

public class Member {

           private int num;

           private String userid;

           private String userpw;

           private String nickname;

          

           public int getNum() {

                       return num;

           }

           public void setNum(int num) {

                       this.num = num;

           }

           public String getUserid() {

                       return userid;

           }

           public void setUserid(String userid) {

                       this.userid = userid;

           }

           public String getUserpw() {

                       return userpw;

           }

           public void setUserpw(String userpw) {

                       this.userpw = userpw;

           }

           public String getNickname() {

                       return nickname;

           }

           public void setNickname(String nickname) {

                       this.nickname = nickname;

           }

          

          

}

 

3.dao 패키지와 클래스와 데이터베이스 테이블을 매핑시키는 xml 파일을 생성

=>member.hbm.xml

 

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE hibernate-mapping PUBLIC

           "-//Hibernate/Hibernate Mapping DTD 3.0//EN"

           "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

 

<hibernate-mapping package="com.pk.db.domain">      

           <class name="Member" table="Member">

                       <id name="num" column="num">

                       </id>

                       <property name="userid" column="userid" />

                       <property name="userpw" column="userpw" />

                       <property name="nickname" column="nickname" />

           </class>

</hibernate-mapping>

 

4.Hibernate 설정 파일을 등록하는 코드를 root-context.xml 파일에 추가

<value>com/pk/db/dao/member.hbm.xml</value>

 

5.Hibernate 사용을 위한 Dao 클래스를 만들고 로그인 관련 메소드를 작성

=>dao/MemberDao

@Repository

public class MemberDao {

           //하이버네이트 사용 관련 객체를 주입받기

           @Autowired

           private SessionFactory sessionFactory;

          

           //로그인 관련 메소드

           //id를 매개변수로 받아서 일치하는 데이터가 있는지 찾아옵니다.

           public Member login(Member member) {

                       //userid 가 기본키가 아니라서

                       //SQL을 이용해서 직접 조회

                       List<Member> list =

                                  (List<Member>)sessionFactory.getCurrentSession()

                                  .createSQLQuery("select * from member where userid=:userid")

                                  .addEntity(Member.class)

                                  .setString("userid", member.getUserid())

                                  .list();

                       //조회된 데이터가 없으면 null을 리턴하고 조회된 데이터가 있으면 데이터를 리턴

                       if(list.size() == 0) {

                                  return null;

                       }else {

                                  return list.get(0);

                       }

           }

}

 

6.home.jsp 파일에 로그인과 로그아웃 링크를 생성

=>로그인에 성공하면 member 라는 속성에 회원정보를 저장할 것임

=>로그인 여부는 session member라는 속성에 데이터가 있으면 로그인 된 것이고 그렇지 않으면 로그인이 되지 않은 것으로 간주

 

           <c:if test="${member == null}">

                       <a href="login">로그인</a><br/>

           </c:if>

           <c:if test="${member != null}">

                       ${member.nickname} <a href="logout">로그아웃</a><br/>

           </c:if>

 

7.login 요청이 GET 방식으로 오면 login 페이지로 포워딩 하도록 메소드를 HomeController에 작성

<%@ page language="java" contentType="text/html; charset=UTF-8"

    pageEncoding="UTF-8"%>

<!DOCTYPE html>

<html>

<head>

<meta charset="UTF-8">

<title>로그인</title>

</head>

<body>

           <form method="post" id="loginform">

                       아이디<input type="text" id="userid" name="userid"

                       required="required"/><br/>

                       비밀번호<input type="text" id="userpw" name="userpw"

                       required="required"/><br/>

                       <input type="submit" value="전송"/>

           </form>

</body>

</html>

 

8.MemberService 인터페이스를 만들고 로그인 처리 메소드를 선언

public interface MemberService {

           //로그인 처리 메소드

           public Member login(HttpServletRequest request);

}

 

9.MemberService 인터페이스를 구현한 MemberServiceImpl 클래스를 만들고 로그인 처리 메소드를 구현

@Service

public class MemberServiceImpl implements MemberService {

           @Autowired

           private MemberDao memberDao;

          

           @Override

           @Transactional

           public Member login(HttpServletRequest request) {

                       Member member = null;

                      

                       //파라미터 읽기

                       String userid = request.getParameter("userid");

                       String userpw = request.getParameter("userpw");

                      

                       //member에 인스턴스를 대입하고 userid를 설정

                       member = new Member();

                       member.setUserid(userid);

 

                       //userid에 해당하는 데이터 찾아오기

                       member = memberDao.login(member);

                       //userid에 해당하는 데이터가 있다면

                       if(member != null) {

                                  if(member.getUserpw().equals(userpw)) {

                                              //로그인 성공한 경우는 session에 로그인 정보를 저장

                                              //member.setUserpw(null);

                                             request.getSession().setAttribute("member", member);

                                  }else {

                                              //로그인 실패

                                              member = null;

                                  }

                       }

                      

                       return member;

           }

 

}

 

10.HomeController에서 login 요청이 POST 방식으로 오면 처리하는 메소드를 생성

1)새로운 서비스를 주입받는 코드를 작성

@Autowired

private MemberService memberService;

 

2)요청을 처리하는 메소드를 생성

@RequestMapping(value="/login", method=RequestMethod.POST)

           public String login(HttpServletRequest request, Model model,

                                  RedirectAttributes attrs) {

                       //RedirectAttributes  redirect 할 때 1번만 사용하는 데이터를

                       //저장할 수 있는 Spring이 제공하는 클래스

                      

                       //서비스의 메소드 호출

                       Member member = memberService.login(request);

                       //로그인 처리도 redirect로 이동

                       if(member == null) {

                                  //로그인 실패의 경우 msg를 저장하고 login으로 다시 이동

                                  attrs.addFlashAttribute(

                                              "msg", "없는 아이디이거나 잘못된 비밀번호입니다.");

                                  return "redirect:login";

                       }else {

                                  //로그인 성공이면 메인 페이지로 이동

                                  return "redirect:./";

                       }

           }

 

11.login.jsp 파일에 로그인 실패 메시지를 출력할 영역을 생성

<div>${msg}</div>

 

12.로그아웃 구현

=>login 된 사실을 Session에 데이터를 저장해서 그 데이터를 확인해서 하게 됩니다.

=>logout Session에 저장한 데이터를 삭제하면 됩니다.

데이터 1개를 삭제할 때는 session.removeAttribute("key");

세션 전체를 삭제하고자 할 때는 session.invalidate();

일정 시간 동안 세션을 사용하지 않으면 세션을 자동삭제하고자 하는 경우에는 web.xml 파일에 session-config 설정을 추가하면 되는데 시간의 단위는 분입니다.

 

1)HomeController 클래스에 logout을 처리하는 메소드를 추가

//로그아웃을 처리하는 메소드

           @RequestMapping(value="/logout", method=RequestMethod.GET)

           public String logout(HttpServletRequest request,

                       HttpSession session) {

                       //Session 객체 만들기

                       //request.getSession() 해도 되고 Controller 의 요청 처리 메소드에

                       //매개변수로 추가해도 됩니다.

                      

                       //세션 초기화

                       session.invalidate();

                       //로그인 과 로그아웃도 redirect를 합니다.

                       return "redirect:./";

           }

 

2)web.xml 파일에 5분동안 세션을 사용하지 않으면 세션을 자동 초기화해주는 설정을 추가

=>모바일 페이지에서는 길게 설정하는 편이고 일반 PC 용 페이지는 조금 짧게 설정합니다.

 

           <!-- 세션을 자동 초기화 해주는 설정 -->

           <session-config>

                       <session-timeout>5</session-timeout>

           </session-config>

 

 

13.메인 화면의 데이터 출력하는 부분을 수정

1)메인화면의 데이터 출력 영역의 크기를 제한

=>출력하는 데이터를 div 태그로 감싸고 div 태그의 높이를 설정한 후 overflow 속성을 이용해서 스크롤 바가 생기도록 만들어주면 됩니다.

           <div id="display" align="center">

                       <table border="1" align="center">

                                  <tr class ="header">

                                              <th width='80'>상품ID</th>

                                              <th width='160'>상품이름</th>

                                              <th width='80'>가격</th>

                                  </tr>

                                  <c:forEach items="${list}" var ="item">

                                              <tr class="record">

                                                         <td align='center'>${item.itemid }</td>

                                                         <td >&nbsp;&nbsp;

                                                                     <%-- <a href="detail?itemid=${item.itemid }">${item.itemname }</a> --%>

                                                                     <a href="detail/${item.itemid}">${item.itemname}</a>

                                                         </td>

                                                         <td align="right">${item.price }&nbsp;&nbsp;</td>

                                              </tr>

                                  </c:forEach>

                       </table>

           </div>

 

2)데이터베이스에서 여러 개의 데이터를 조회

=>데이터베이스는 순차적으로 저장하는 List 구조가 아닙니다.

=>여러 개의 데이터를 조회할 때는 정렬을 고려해야 합니다.

 

3)데이터베이스의 데이터를 조회하는 서비스를 구현

=>자주 변경되는 데이터가 아닌 경우 - 클라이언트가 요청할 때 마다 애플리케이션 서버가 DB서버에게 sql 을 전송해야 하는가?

=>애플리케이션 서버가 DB에게 주기적으로 query를 전송하고 클라이언트의 요청은 애플리케이션 서버의 데이터로 처리하거나 클라이언트에 저장해두고 처리 하는 방식?

 

=>ItemServiceImpl 클래스의 allItem 메소드를 수정

public List<Item> allItem(HttpServletRequest request) {

           //Dao의 메소드에 파라미터가 없는 경우는 Dao 메소드를 호출해서 리턴

           //return itemDao.allItem();

           List<Item> list = hibernateDao.allItem();

           //list의 데이터를 정렬할 때는 list.sort()를 호출하면 되는데 이 경우는

//list에 속한 데이터에 Comparable 인터페이스가 implements 되어 있어야 합니다.

//그렇지 않은 경우는 Comparator 인터페이스를 구현한 인스턴스를 대입해주어야 합니다.

                       //itemid의 내림차순 - itemid는 정수

                       Comparator<Item> comp = new Comparator<Item>() {

                                  @Override

                                  public int compare(Item arg0, Item arg1) {

                                              // TODO Auto-generated method stub

                                              return arg1.getItemid() - arg0.getItemid();

                                  }

                       };

                       list.sort(comp);

                       return list;

           }

 

 

 

 

**AOP(관점 지향 프로그래밍 - Aspect Orientied Programming)

=>공통관심사항(common - cross cutting concern) 과 비지니스로직(core concern)을 분리해서 구현한 후 설정파일이나 어노테이션을 이용해서 결합을 시켜나가는 프로그래밍 방식

=>복잡한 로직을 구현할 때 이 방식을 취합니다.

파이썬에서도 어노테이션을 이용해서 이러한 구현이 어느정도 가능합니다.

=>spring에서는 url에 반응하는 HandlerInterceptor method 호출에 반응하는 AOP 2가지 형태로 구현

 

**HandlerInterceptor

=>AOP 구현을 위해 제공되는 인터페이스

1.메소드

1)preHandle: Controller의 요청 처리 메소드를 호출하기 전에 호출되는 메소드로 이 메소드에서 true를 리턴하면 Controller의 요청 처리 메소드로 이동하고 false를 리턴하면 Controller의 요청 처리 메소드로 가지 않습니다.

 

2)postHandle: Controller의 요청 처리 메소드가 처리를 전부 수행하고 View로 이동하기 직전에 호출되는 메소드로 예외가 발생하면 호출되지 않습니다.

 

3)afterCompletion: 요청을 전부 처리하고 난 후 호출되는 메소드로 예외 발생여부에 상관없이 무조건 호출되는 메소드

 

2.설정

1)모든 요청에 반응

<mvc:interceptors>

           <ref bean="인터셉터의 id"/>

</mvc:interceptors>

 

2)특정 URL 패턴에 반응

<mvc:interceptors>

           <mvc:interceptor>

                       <mvc:mapping path="URL 패턴"/>

                       <ref bean="인터셉터의 id"/>

           </mvc:interceptor>

</mvc:interceptors>

 

=>mvc:mapping ref bean 의 순서를 변경하면 안됨

 

3.chat 요청으로 이동할 때 로그인이 되어 있지 않으면 로그인 페이지로 이동하도록 interceptor를 생성

1)HandlerInterceptor 인터페이스를 implements 한 클래스를 기본 패키지 안에 생성

=>com.pk.db.util.AuthInterceptor

//클래스 이름에 Adapter가 붙는 클래스는

//Adaptor를 제외한 인터페이스가 존재하는데

//인터페이스는 모든 메소드를 재정의 해야 하고 Adapter 클래스는 필요한 메소드만

//재정의하면 됩니다.

//Bean을 자동 생성해주는 어노테이션

@Component

public class AuthInterceptor extends HandlerInterceptorAdapter {

           @Override

           public boolean preHandle(

                       HttpServletRequest request,

                       HttpServletResponse response,

                       Object handle) {

                       boolean result = false;

                      

                       //로그인이 안된 것을 확인

                      if(request.getSession().getAttribute("member") != null) {

                                  result = true;

                       }else {

                                  try {

                                              //로그인이 되어 있지 않으면 로그인 페이지로 이동

                                              response.sendRedirect("login");

                                  } catch (IOException e) {

                                              // TODO Auto-generated catch block

                                              e.printStackTrace();

                                  }

                       }

                       return result;

           }

          

           @Override

           public void postHandle(

                       HttpServletRequest request,

                       HttpServletResponse response,

                       Object handle,

                       ModelAndView mav) {

                       System.out.println("Controller의 요청을 정상적으로 처리");

                       //로그 기록하는 경우가 많습니다.

           }

}

 

2)servlet-context.xml 파일에 interceptor 설정하는 코드를 추가

<!-- interceptor 설정 : chat으로 이동할 때 interceptor가 동작하도록 설정 -->

           <interceptors>

                       <interceptor>

                                  <mapping path="/chat"/>

                                  <beans:ref bean="authInterceptor" />

                       </interceptor>

           </interceptors>

 

4.chat.jsp파일에서 이름을 입력하지 않고 로그인 한 유저의 nickname을 이용하기

=>로그인을 하면 sessionmember라는 이름으로 Member객체가 저장되어 있음

자바스크립트에서 메시지를 전송하는 send메소드를 수정

           <!-- 이름<input type="text" id="nickname"/> --> 주석처리

 

 

                       var send = function(){

                                  //입력한 내용을 websocket서버에게 전달하고 message 란은 클리어

                                  //var nickname = document.getElementById("nickname").value;

                                  var nickname = '${member.nickname}';

                                  var msg = document.getElementById("message").value;

                                  wsocket.send(nickname+":"+msg);

                                  document.getElementById("message").value="";

                       }

 

5.chat으로 이동할려고 했는데 로그인이 되어 있지 않아서 로그인 페이지로 이동한 후 로그인에 성공하면 chat으로 이동하도록 해주는 설정을 추가

=>로그인 페이지로 이동할 때 메시지 저장해서 이동

=>인터셉터에서 이동할려고 했던 주소를 세션에 저장

=>로그인 성공했을 때 세션에 저장된 데이터를 확인해서 그 페이지로 이동하도록 해주어야 합니다.

 

1)    인터셉터의 preHandle메소드 수정

AuthInterceptor.java

           // Controller 가기 전에 호출되는 메소드

           @Override

           public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handle) {

                       boolean result = false;

                       // 로그인 안된 것을 확인

                       if (request.getSession().getAttribute("member") != null) {

                                  // true를 리턴하도록 만들면 원래 처리를 하러 이동

                                  result = true;

                       } else {

                                  try {

                                             

                                              //로그인 페이지로 이동할 때 메시지 저장

                                              request.getSession().setAttribute("msg", "로그인이 되어야 가능한 서비스입니다.");

                                              //요청 url을 확인

                                              String requestURI = request.getRequestURI();

                                              String contextPath = request.getContextPath();

                                              String command = requestURI.substring(contextPath.length());

                                             

                                              //파라미터 가져오기

                                              String queryString = request.getQueryString();

                                              //파라미터가 있으면 command뒤에 붙이기

                                              if(queryString != null) {

                                                         command = command +"?"+queryString;

                                              }

                                              //세션에 command저장

                                              request.getSession().setAttribute("dest", command);

                                              // 로그인 되어 있지 않으면 로그인 페이지로 이동

                                              response.sendRedirect("login");

                                  } catch (IOException e) {

                                              e.printStackTrace();

                                  }

 

                       }

                       return result;

           }

 

클릭시 =>

 

2)HomeControllerloginPOST로 처리하는 메소드를 수정

=>login성공했을 때 sessiondest값이 있으면 dest로 이동하도록 만들어 주저야 합니다.

이동한 후에는 dest값을 삭제해야 합니다.

           @RequestMapping(value = "/login", method = RequestMethod.POST)

           public String login(HttpServletRequest request, Model model, RedirectAttributes attrs) {

                       // RedirectAttributes redirect를 할 때 한번만 사용하는 데이터를 저장할 수 있는 spring이 제공하는 클래스

 

                       // 서비스의 메소드를 호출

                       Member member = memberService.login(request);

                       // 로그인 처리도 redirect로 이동

                       if (member == null) {

                                  // 로그인 실패의 경우

                                  attrs.addFlashAttribute("msg", "없는 아이디이거나 잘못된 비밀번호입니다.");

                                  return "redirect:login";

                       } else {

                                  //이동할 url이 있는지 확인

                                  String dest = (String) request.getSession().getAttribute("desc");

                                  if(dest != null) {

                                              return "redirect:"+dest;

                                  }

                                  // 로그인 성공이면 메인 페이지로 이동

                                  return "redirect:./";// main페이지로 이동

                       }

 

           }

 

 

**springaop

=>메소드의 수행 전이나 수행 후에 공통된 내용을 수행하고자 하는 경우 사용

=>InterceptorURL에 대해서 반응하기 때무에 웹 프로그래밍에서만 사용이 가능하지 만 AOP는 메소드에 반응하기 떄문에 모든 프로그래밍에서 사용이 가능

AOP를 적용한 클래스를 상속받아서 SPRING이 별도의 클래스를 만들고 인스턴스를 만들어서 메소드 호출을 하도록 합니다.

FINAL CLASS에는 AOP 적용이 안됩니다.

 

=>구현 가능한 Advice(메소드 호출 시점)

Before Advice:메소드 호출전

After Returing Advice:메소드가 수행을 종료하고 리턴 한 후

After Throwing Advice:메소드 수행중 예외가 발생한 경우

After Advice:메소드 수행 후

Around Advice: 메소드 수행 전과 수행 후

 

 

=>구현 방법

xml기반의 pojo클래스 또는 MethodInterceptor 인터페이스를 이용한 구현

@Aspect Annotation을 이용하는 방법

 

 

=>xml 기반이 먼저 만들어 졌고 나중에 어느테이션이 추가된 것입니다.

 

=>필요한 의존성 라이브러리

aspectjweaver, spring-aop, spring-aspects

 

1.xml 스키마를 이용한 aop 설정 방법

1)pom.xml 파일에 aop를 위한 의존성을 추가

2)공통관심 사항을 가진 클래스를 생성해서 메소드를 추가

3)spring설정 파일에 <aop:config>태그를 이용해서 aspect를 설정

4)Advice를 어떤 point cut에 설정을 할 것인지를 지정

 

2.실습

1). aspectjweaver, spring-aop, spring-aspects

 

                       <!-- aop사용을 위한 의존성 라이브러리 -->

                       <dependency>

                                  <groupId>org.aspectj</groupId>

                                  <artifactId>aspectjweaver</artifactId>

                                  <version>1.8.8</version>

                       </dependency>

                       <dependency>

                                  <groupId>org.springframework</groupId>

                                  <artifactId>spring-aop</artifactId>

                                  <version>${org.springframework-version}</version>

                       </dependency>

                       <dependency>

                                  <groupId>org.springframework</groupId>

                                  <artifactId>spring-aspects</artifactId>

                                  <version>${org.springframework-version}</version>

                       </dependency>

 

 

2)Advice(공통 관심 사항을 가진 객체)로 사용할 클래스를 생성하고 메소드 작성

=>기본 패캐지 안에 생성

=>com.pk.db.util.LoggingAdvice

//spring에서는 dto클래스를 제외하고는 클래스 이름 위에 어노테이션을 추가해서

//bean을 자동 생성하도록 합니다.

@Component

public class LoggingAdvice {

           //리턴 타입과 매개변수는 변경할 수 없습니다.

           public Object invoke(ProceedingJoinPoint joinPoint) {

                       //현재 시간을 출력

                       Calendar cal = new GregorianCalendar();

                       Date date = new Date(cal.getTimeInMillis());

                       System.out.println(date+" 요청이 발생함");

                      

                       Object obj = null;

                      

                       try {

                                  obj = joinPoint.proceed();

                       } catch (Throwable e) {

                                  // TODO Auto-generated catch block

                                  e.printStackTrace();

                       }

                       return obj;

           }

}

 

3)servlet-context.xml파일에 aop 네임스페이스를 추가

 

4)servlet-context.xml파일에 aop설정 태그를 추가

       <!-- aop설정

           com.pk.db패키지  내에 있는 Controller 라는 이름으로 끝나는 클래스의

           메소드이름은 상관없고 매개변수갯도 상관없이 호출만 되면

           LoggingAdvice클래스의 invoke메소드가 호출됩니다.-->

           <aop:config>

                       <!-- <aop:aspect id="traceAspect" ref ="Advice 클래스의 bean id"></aop:aspect> -->

                       <aop:aspect id="traceAspect" ref ="loggingAdvice">

                                  <!-- <aop:pointcut expression="동작할 advice " id=""/> -->

                                  <aop:pointcut expression="execution(public * com.pk.db.*Controller.*(..))" id="publicMethod"/>

                                  <aop:around pointcut-ref="publicMethod" method="invoke"/>

                       </aop:aspect>

           </aop:config>

 

 

3.Aspect어노페이션을 이용하는 방법

1)java 클래스에 @Aspect 라는 어노테이션을 추가

2)수행할 메소드 위에 execution을 작성

 

3)스프링 설정 파일에 aop:aspectj-autoproxy태그를 설정

 

4.실습

=>Dao 클래스의 메소드를 호출할 때 현재 시간과 데이터베이스에 접근했다고 로그를 기록

 

 

1)기본 패키지 안에 Advice로 사용할 클래스를 생성 하고 메소드 작성

=>기본패키지.util.LoggingDao

@Component

//aop 클래스라는 어노테이션

@Aspect

public class LoggingDao {

          

           @Around("execution(public * com.pk.db..*Dao.*(..)")

           public Object invoke(ProceedingJoinPoint joinPoint) {

                       // 현재 시간을 출력

                       Calendar cal = new GregorianCalendar();

                       Date date = new Date(cal.getTimeInMillis());

                       System.out.println(date + "  데이터베이스에 접근");

 

                       Object obj = null;

                       try {

                                  obj = joinPoint.proceed();

                       } catch (Throwable e) {

                                  e.printStackTrace();

                       }

                       return obj;

           }

}

 

 

 

2)servlet-context.xml파일에 autoproxy설정 추가

       <!-- 어노테이션을 추가한 aop 클래스가 동작하도록 해주는 설정 -->

           <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

 

 

 

vue,angular.react ->출력

 

openAPI 활용 - 날씨 ,지도

 

자료구조

알고리즘

 

MONGO DB  + node.js

자바에서 mongo db사용할 빠에는 카산들라 로 사용하는 것이 좋다.

 

빅데이터 처리

java ->scala

è  kotlin -안드로이드 / 서버

scala는 안드로이드 를 못한다. 빅데이터 처리쪽은 사용한다.

pythonc-python이다.

 

 

 

python지우고 anaconda 설치

 

 

형상관리 - 변경 사항을 관리

형상관리 -TOOL -GIT HUB

 

SERVELTE:JAVA클래스에 HTML을 추가하는 방식., 첫번째 요청이 오면 컴파일을 해서 클래스를 만들고 인스턴스를 만들어서 이 인스턴스 1개를 자기고 모든 요청을 처리

 

JSP: HTML페이지에 JAVA코드를 추가하는 방식 , 요청이 올 때마다 SERVLET클래스를 만들고 인스턴스를 만들어서 요청을 처리하고 삭제됩니다.

 

REQUEST: 클라이언트의 요청

RESPONSE: 클라이언트의 요청에 대한 서버의 응답

 

파라미터 : 웹 프로그래밍에서 요청한 클라이언트가 넘겨준 데이터

 

JUNIT

AOP-관점 지향 크로그래밍 비즈니스 로직과 공통 관심 사항을 분리해서 프로그래밍 하자는 프로그래밍 방식은 ?

 

 

 

웹표준 : 누구나 동일한 콘텐츠를 사용할 수 있어야 한다.

크로스 브라우징 모든 브라우저에서 동일한 콘텐츠를 사용할 수 있어야 한다.

반응형 웹 디자인: 화면 크기에 상관없이 동일한 콘텐츠를 사용할 수 있어야 한다.

 

N- screen :디바이스 종류에 상관없이 동일한 콘텐츠를 사용할 수 있어야 한다.

 

 

ajax : 비동기적으로 서버에서 데이터를 받아오는 자바스크립트 기술

화면 전체를 갱신하지 않고 데이터를 받아서 화면의 일부분만 갱신하는 기술

 

 

open api 의 표준 데이터 포맷

 

-json: 자바스크립트의 데이터 표현 방법

-xml: 태그를 이용한 데이터 표현 방법

 

내부보안 : 건물안에서

시설보안 : 건물

운영보안 : 운영하면서

 

 

소프트웨어는 비용으로 썬다.

 

white box test:내부 구조를 테스트, 반복문이나 조건문 등을 테스트

black box test:기능 테스트 ., 입력이 주어지면 올바른 결과가 나왔는지만 테스트

 

validation test(검증 시험)

alpha test :개발자의 장소에서 사용자가 테스트

beta test : 사용자의 장소에서 사용자가 테스트

 

 

sqlmapper: sql과 자바코드의 분리 - 대표적으로 mybatis

orm: 테이블과 클래스를 매핑, 테이블의 행과 객체를 매핑 - 대표적으로 hiberante

 

=>si업계는 sqlmapper솔루션 업체는 orm

si는 남들이 제작해달라고 하는 소프트웨어를 개발 - erp(전사적 자원관리)

solution:특정 문제를 해결하기 위한 소프트웨어

           비슷한 유형의 프로젝트를 여러번 했기 때문에 솔루션을 개발

 

@Controller

 

websocket: web에서 연결형 통신(tcp)을 구현하기 위한 html5 api

 

authentication(인증 ) : 로그인과 관련된 부분

 

authorization: (인가) : 사용권한과 관련된 부분

 

di(의존성 주입)내부에서 사용할 데이터를 외부에서 생성해서 대입받아서 사용

@Autowird가 의존성 주입을 위한 어노테이션

 

개체 무결성 : 기본키는 null이거나 중복될 수 없다.

 

springbean을 자동생섷해주는 어노테이션 4

 

 

어노테이션은 클래스이다.

첫글자 대문자 : class 생성자 예: Sa() -> 생성자

첫글자 소문자 : 메소드        sa()->일하는 것

 

 

텐스플로는 64bit용이여야 한다.

 

 

 

 

python개발환경 3가지 방법

python

anaconda

테노피

 

 

python은 버전이 다르면 계속 설치된다. 자바도 그렇다.

 

idespyder 혹은 / pycharm

jupyter

 

 

반응형

'Study > spring' 카테고리의 다른 글

spring-8  (0) 2020.10.26
spring-6  (0) 2020.10.26
spring-5  (0) 2020.10.23
spring-4  (0) 2020.10.23
spring-3  (0) 2020.10.23

+ Recent posts