55일차 공부
** ajax와 REST API Server 구축 및 암호화
1. REST API
=> 디바이스 나 화면 크기에 상관없이 동일한 콘텐츠를 사용하는 경우에는 하나의 URL을 이용
=> Web Browser, Android Application, iOS Application 등이 동일한 콘텐츠를 이용하는 경우라면 동일한 URL을 이용해서 콘텐츠를 사용할 수 있어야 한다.
=> 서버 입장에서 여러 디바이스의 요청을 서로 다른 URL을 이용해서 처리하게 되면 유지보수도 어렵고 서버 Application 을 여러개 구현해야 할 가능성이 높기 때문에 관리가 어려워진다.
=> 클라이언트 개발자 입장에서는 디바이스 별로 다른 URL을 요청하기 때문에 클라이언트용 애플리케이션 개발이 어려워진다.
=> 이 경우 선택한 데이터 포맷은 XML과 JSON 이다
XML은 태그 형식으로 표현하기 때문에 개발자가 구조를 파악하는게 쉽다.
JSON은 자바스크립트 데이터 표현방법으로 표현하기 때문에 개발자가 구조를 파악하는게 어렵고 나중에 나온 포맷이라서 외부 라이브러리를 이용해야만 쉽게 사용할 수 있는 경우가 많은데 XML 보다는 경량이고 자바스크립트나 파이썬 같은 언어와 데이터 작성 방법이 동일하기 때문에 파싱이 수월하다.
=> Java Web Programming 만으로 가능하지만 Spring 같은 Framework를 사용하면 조금 더 쉽게 구현한다.
=> Server가 View를 만들지 않고 데이터를 만들어서 전송하는 개념이다.
2. ajax
=> 비동기 데이터 전송
=> REST API Server는 View를 전송하는것이 아니라 데이터를 전송해주기 때문에 HTML 파일에서 Server 와 통신하기 위해서 이전에 사용하던 a태그나 자바스크립트의 페이지이동 또는 iframe을 사용할 수 없다.
=> REST API Server와 통신하기 위해서는 다른 기술을 사용해야 하는데 그 중의 하나가 ajax이다.
=> ajax는 자신의 도메인 데이터만 가져올 수 있도록 설계되어 있는데 서버 측에서 CORS가 가능하도록 설계해주면 ajax를 이용해서 다른 도메인의 데이터도 가져 올 수 있다.
상대방 서버가 CORS가 가능하도록 설계되어 있지 않다면 proxy을 이용해서 ajax 요청을 자신의 서버에게 보내고 서버용 프로그래밍 언어를 이용해서 외부 데이터를 가져오고 다시 ajax에 전달하는 방식을 이용해야 한다.
3. 암호화
=> 암호화된 평문을 그냥은 알아볼 수 없도록 만드는 것
=> 암호화된 문장은 평문으로 변경하는 것을 복호화
1) 암호화 방식
=> 평문을 비문으로 만든 후 비문을 평문으로 복호화 할 수 없도록 하는 대신에 평문과 비문을 비교할 수 있도록 하는 방식으로 비밀번호를 저장한다.
=> 비문을 평문으로 복호화 할 수 있도록 만드는 방식
2) 암호화 알고리즘
=> md5, sha-256, res-256 등 여러가지 알고리즘이 있다.
** 더보기 구현
1. json 생성을 위한 라이브러리를 프로젝트에 복사
=> view에 데이터를 전달하기 위해서 수행
2. ServiceImpl 클래스에서 데이터 목록을 만들어주는 메소드를 수정
@Override
public void list(HttpServletRequest request, HttpServletResponse response) {
//1.파라미터 읽기
//페이지 번호와 페이지 당 데이터 개수를 저장할 변수를 생성
int pageno = 1;
int perpagecnt = 5;
//파라미터로 페이지 번호와 페이지 당 데이터개수가 넘어오면
//페이지 번호와 페이지당 데이터 개수 변경
String no = request.getParameter("no");
String pagecnt = request.getParameter("pagecnt");
if(no != null) {
pageno = Integer.parseInt(no);
}
if(pagecnt != null) {
perpagecnt = Integer.parseInt(pagecnt);
}
//2.파라미터 변환 작업이나 알고리즘 처리
//3.호출할 Dao 메소드의 매개변수를 생성
//4.Dao의 메소드를 호출해서 결과를 저장
//JSON 출력을 위해서 JSON 데이터 형식으로 변환
//List,배열 - > JSONArray
//DTO 나 Map -> JSONObject
List<Item> list = itemDao.list(pageno, perpagecnt);
JSONArray ar = new JSONArray();
for(Item item : list) {
JSONObject obj = new JSONObject();
obj.put("code", item.getCode());
obj.put("title", item.getTitle());
obj.put("category", item.getCategory());
obj.put("description", item.getDescription());
ar.put(obj);
}
//전체 데이터 개수를 가져오기
int totalCount = itemDao.getCount();
//데이터 출력화면에 표시할 마지막 페이지 번호와 시작 페이지 번호를
//생성
//하나의 페이지에 페이지 번호를 10개씩 출력
//종료 페이지 번호를 임시로 계산
//1 - 10, 2 - 10, 12 - 20, 21 - 30
int endPage = (int)(Math.ceil(pageno/10.0)*10.0);
//페이징에서는 뒤로 가는게 있어서 시작 페이지 번호가
//필요하지만 더보기 형태의 구현은 뒤로 가는게 없어서
//시작 페이지 번호가 필요 없습니다.
//int startPage = endPage - 9;
//전체 페이지 개수 구하기
int tempEndPage = (int)(Math.ceil(totalCount/(double)perpagecnt));
//끝나는 페이지 번호가 전체 페이지 개수보다 크면 끝나는 페이지 번호 수정
if(endPage > tempEndPage) {
endPage = tempEndPage;
}
//이전도 필요가 없습니다.
//이전과 다음의 출력 여부 생성
//boolean prev = startPage == 1 ? false : true;
//다음 데이터의 존재여부를 위해서 있는 것이 좋음
boolean next = endPage * perpagecnt >= totalCount ? false : true;
//5.Dao 메소드 호출 결과를 View로 전달하기 위해서 request 나 session 에 저장
//포워딩 할 거면 request
//리다이렉트 할 거면 session
/*
request.setAttribute("list", list);
request.setAttribute("startpage", startPage);
request.setAttribute("endpage", endPage);
request.setAttribute("pageno", pageno);
request.setAttribute("prev", prev);
request.setAttribute("next", next);
*/
//REST API 서버를 구현할 때는 JSONObject로
//묶어서 1개만 저장
JSONObject result = new JSONObject();
//현재 페이지 번호, 종료 페이지 번호, 다음 존재여부
//출력할 데이터
result.put("pageno", pageno);
result.put("endpage", endPage);
result.put("next", next);
result.put("ar", ar);
//request에 저장
request.setAttribute("result", result);
}
3. Controller를 확인해서 list 요청이 왔을 때 list.jsp로 출력하는 것을 확인하고 list.jsp 파일을 만들어서 앞에서 저장한 내용을 출력
4. 테스트
=> POST 방식은 테스트가 어렵지만 GET 방식은 브라우저를 이용해서 테스트가 가능
=> 서버를 실행시키고 브라우저 창에 item/list를 추가해서 확인
=> 여기까지가 서버 구현에서는 끝
5.Web Client 에서 데이터를 요청해서 출력하기
1) index.jsp 페이지에서 목록 보기 요청을 수정
<li><a href="item/itemlist">전체 데이터 조회</a></li>
2) ItemConroller에서 itemlist 요청이 온 경우 출력할 페이지로 포워딩하는 코드를 doGet 메소드에 작성
else if(command.equals("/item/itemlist")) {
RequestDispatcher dispatcher = request.getRequestDispatcher("../view/itemlist.jsp");
dispatcher.forward(request, response);
}
3) doGet 메소드에서 삽입이나 수정, 삭제 이후에 이동하는 곳도 itemlist로 변경
4) itemlist.jsp 파일을 생성하고 출력
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>목록 보기</title>
</head>
<body>
<h2 align="center">목록 보기</h2>
<table align="center" border="1" id="table">
<tr>
<th>코드</th>
<th>카테고리</th>
<th>제목</th>
</tr>
</table>
</body>
<!-- jquery를 사용하기 위한 링크 설정
ajax를 편리하게 사용하기 위해서 -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script>
//페이지 번호 변수
var pageno = 1;
//ajax 요청을 해서 table에 데이터를 출력해주는 함수
function adddata(){
$.ajax({
url:"list",
dataType:"json",
data:{"no":pageno},
success:function(data){
//더보기 버튼을 삭제
$('#add').remove();
//배열을 순회해서 하나의 객체를 한 줄로 출력
$.each(data.ar, function(index, item){
disp = "<tr><td>" + item.code + "</td>";
disp += "<td>" + item.category + "</td>";
disp += "<td>" + item.title + "</td></tr>";
$('#table').html($('#table').html() + disp);
});
//더보기 버튼 만들기
//현재 페이지가 종료 페이지보다 작을 때 만 생성
if(data.pageno < data.endpage){
//페이지 번호 하나 올리기
pageno = pageno + 1;
disp = "<tr id='add'>" +
"<td colspan='3' align='center'>" +
"더보기" + "</td></tr>";
$("#table").html($("#table").html() + disp);
//id 가 add 객체를 click 하면 adddata 라는 함수를 호출
document.getElementById("add")
.addEventListener("click", adddata);
}
}
});
};
//태그를 전부를 읽고 난 후 수행
window.addEventListener("load", function(e){
adddata();
})
</script>
</html>
** 회원 가입과 로그인
=> 회원가입과 로그인을 REST API SERVER를 구축해서 구현
=>회원가입 화면을 만드는 부분이나 회원가입 요청을 ajax로 하는 부분 등 HTML 출력하는 부분을 제외하면 모든 처리에서 사용할 수 있는 서버
=>파일 업로드도 같이 처리 : 파일 업로드는 구현하고 난 후 상대경로를 이용하는 것으로 변경
1. 데이터베이스 작업
1) 테이블 생성
2) 샘플 데이터를 작성
=> 아이디 중복 검사와 닉네임중복 검사를 위해서 작성
3) 테스트가 종료되면 샘플 데이터를 삭제
=> 데이터를 삽입할 때는 비밀번호를 암호화해서 삽입할 것인데 이전에 입력한 데이터는 암호화가 되지 않은 상태이므로 로그인이 안되기 때문에
2. Dynamic Web Application 을 생성
=> web.xml(웹 프로젝트 설정 파일 - Web Application이 실행되면 이 파일의 내용을 읽어서 프로젝트 전체를 설정한다. 이 파일이 잘못되면 Application이 시작되지 않는 경우가 많다) 파일을 포함하도록 생성 - 포함시키지 않았으면 Servers 디렉토리에서 복사를 해서 WebContent/WEB-INF 디렉토리에 붙여넣기
=> WebContent는 루트 디렉토리의 이름으로 프로젝트 종류에 따라 다른 이름 일 수 있다.
=> 실제 배포가 될 때는 루트 디렉토리는 애플리케이션 이름으로 대체된다.
3. 필요한 의존성 라이브러리들을 프로젝트에 복사
=> Dynamic Web Application 은 라이브러리들이 WebContent/WEB-INF/lib 디렉토리에 복사가 되어야 한다.
=> servlet-api.jar : JDK SE 버전을 가지고 웹 프로그래밍을 할려면 HttpServlet 클래스가 존재하는 라이브러리가 필요 - WAS가 가지고 있기 때문에 배포를 할 때는 필요 없다.
=> jstl.jar : JSTL(c:if, c:forEach 등)을 사용하기 위해서 필요
웹 서버만을 만드는 경우에는 필요없다.
jsp파일에서 attribute를 출력하기 위해서 사용하는 라이브러리이기 때문에 출력을 하지 않을거라면 필요없다.
=> json.jar : 자바의 데이터를 json포맷으로 만들어주는 라이브러리
=> mysql-connector : MySQL 사용을 위한 라이브러리
4. 프로젝트 공통 설정
=> 데이터베이스 접속 정보 설정
1) META-INF 디렉토리에 context.xml
5. User 테이블에 발생하는 요청을 처리할 자원들을 생성
=> url 패턴은 user 디렉토리 패턴을 이용 : 예전에는 디렉토리 패턴이 아니라 확장자 패턴을 많이 사용했는데 최근에는 디렉토리 패턴을 많이 이용 - /user/*
=> view는 member라는 디렉토리에 저장 : view와 디렉토리 패턴이 동일하게 만들어지면 view로 이동하는 경우에도 Controller가 작업을 수행할려고 하기 때문에 되도록이면 서로 다른 다렉토리를 이용하는 것이 좋다.
=> 필요한 자원은 DTO, DAO, Service, Controller 클래스가 필요하다.
=> view 디렉토리는 실제 view와 그 이외 자원들이저장되는 디렉토리로 구분하며 그 이외 자원으로는 css, js, 이미지, 사운드, 등영상 등이다.
resources 라는 디렉토리를 만들고 그 안에 다시 각각이 디렉토리를 만들어서 자원을 저장
=> 만드는 순서는 상관은 없지만 DAO와 Service와 Controller 는 순서대로 만든다.
DAO를 주입받아서 Service가 사용하고 Service를 주입받아서 Controller가 사용하기 때문이다.
2) web.xml 파일에 context.xml 파일의 내용을 읽어오는 코드를 작성
<!-- context.xml 파일의 내용을 읽어내는 설정-->
<resources-ref>
<description>Connection</description>
<res-ref-name>DBConn</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resources-ref>
=> 프로젝트 설정인데 여러 명의 개발자가 협업을 하는 경우에는 여기까지는 지금처럼 설명만하고 프로젝트를 미리 만들어 두고 다운로드 받아서 사용하도록 한다.
1) User 테이블의 데이터를 저장할 DTO 클래스를 생성
=> domain.User
package domain;
import java.sql.Date;
public class User {
private String email;
private String password;
private String nickname;
private String islogin;
private Date logindate;
private String isremove;
public User() {
super();
// TODO Auto-generated constructor stub
}
public User(String email, String password, String nickname, String islogin, Date logindate, String isremove) {
super();
this.email = email;
this.password = password;
this.nickname = nickname;
this.islogin = islogin;
this.logindate = logindate;
this.isremove = isremove;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public String getIslogin() {
return islogin;
}
public void setIslogin(String islogin) {
this.islogin = islogin;
}
public Date getLogindate() {
return logindate;
}
public void setLogindate(Date logindate) {
this.logindate = logindate;
}
public String getIsremove() {
return isremove;
}
public void setIsremove(String isremove) {
this.isremove = isremove;
}
@Override
public String toString() {
return "User [email=" + email + ", password=" + password + ", nickname=" + nickname + ", islogin=" + islogin
+ ", logindate=" + logindate + ", isremove=" + isremove + "]";
}
}
2) 모든 DAO 클래스에서 공통으로 사용할 내용을 소유한 AbstractDAO 클래스를 생성하고 공통된 내용을 작성
=> dao.AbstractDao
=> 원래 이 클래스는 애플리케이션을 만들면서 리팩토링 단계에서 만들어져야 하지만 미리 생성 - 이런 클래스를 만들어서 상속받아서 사용하도록 해주는 것이 라이브러리나 프레임워크이다.
=> 이 클래스는 인스턴스를 만들 필요가 없어서 추상 클래스로 만들것이고 다른 패키지에서 사용을 못하도록 하기 위해서 default(package)라는 접근 지정자를 사용
=> 공통으로 필요한 내용은 Connectionm PreparedStatement, ResultSet3개의 속성과 연결과 해제와 관련된 2개의 메소드 : 접근 지정자는상속받은 곳에서 사용할 수 있도록 할려면 private은 제외
=> 자신만의 application을 만들 때는 접근지정자를 default로 하고 프레임워크를 만들 때는 protected로 한다.
package dao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.sql.DataSource;
public class AbstractDao {
//데이터베이스 연동에 필요한 속성
Connection con;
PreparedStatement pstmt;
ResultSet rs;
//데이터베이스 연결 메소드
void connect() {
try{
//web.xml 파일에서 데이터베이스 접속보를 가져오기
Context context = new InitialContext();
DataSource ds = (DataSource)context.lookup("java:comp/env/DBConn");
//직접 접속하는 것이 아니라 이미 접속된 연결 객체를 빌려와서 사용한다.
con = ds.getConnection();
}catch(Exception e) {
System.out.println(e.getLocalizedMessage());
e.printStackTrace();
}
}
//데이터베이스 자원을 해제하는 메소드
void close() {
try {
if(rs!=null) {
rs.close();
}
if(pstmt!=null) {
pstmt.close();
}
if(con!=null) {
con.close();
}
}catch(Exception e) {
System.out.println(e.getLocalizedMessage());
e.printStackTrace();
}
}
}
3) User 테이블의 데이터베이스 작업을 위한 UserDao 클래스를 생성
=> AbstractDao 클래스를 상속하도록 생성
=> dao.UserDao
package dao;
public class UserDao extends AbstractDao {
//싱글톤 패턴을 적용하기 위한 코드
//인스턴스를 하나만 생성하는 디자인 패턴
//모든 곳에서 공유할 데이터를 갖는 클래스나 Entry Point(출입구)에 해당하는 클래스 또는
//서버에서 클라이언트의 요청을 처리하는 클래스는 인스턴스가 1개이면 된다.
private UserDao() {}
private static UserDao userDao;
public static UserDao sharedInstance() {
if(userDao ==null) {
userDao =new UserDao();
}
return userDao;
}
}
** Java 로 취업
=> Spring Framework 와 JPA 를 이용해서 Java Web Server 를 구축해서 연동
=> SI에서는 JPA 대신에 MyBatis를 사용한다.
=> SI에서는 데이터베이스도 Oracle을 많이 사용
** SI - 전자정부 프레임워크 , 삼성, SK 또는 금융 관련 분야
Java -> Spring -> MyBatis + Oracle
위의 프로그램을 이용해서 게시판 정도 구현하면 일을 할 수 있다.
회원 : ajax(아이디 중복 검사) - REST API, Session(로그인), 보안(암호화, JWT)
게시판 : 페이징, 보안(Authentication, Authorization)
1. 개발 환경
Programming Language : JAVA 1.8
Database : MySQL 5.5
Web Application Server : Apache Tomcat 9.0
IDE : Eclipse
Framework : Spring, Hibernate, jQuery...
형상관리도구 : Git Hub
2. E-R Diagram
3. Class Diagram
4. 인터페이스 - 요청 처리 흐름
5. 보완할 점
** Inheritance(상속)
=> 상위 클래스의 모든 멤버를 하위 클래스가 물려받는 것
=> 상속을 하는 이유
1. 프로그램을 만들다 보니 여러 클래스에서 중복된 내용이 나오는 경우 상위 클래스를 만들어서 상속을 시키면 코드의 중복을 제거 : Abstract Class(추상 클래스)
=> 상속을 클래스 다이어그램에서 는 하위 클래스에서 상위 클래스로 향하는 화살표로 표현한다.
2. 프레임워크의 클래스를 상속하는 경우프레임워크가 제공하는 클래스는 기능이 제한적이라서 기능을 확장하고자 하는 경우에 한다.
=> SubClassing 이라고 하고 이 경우는 대부분 Overriding을 이용해서 메소드의 기능을 확장시킨다.
=> 제공되는 클래스의 경우 인스턴스만 생성해서 사용할 수 있음에도 상속을 하는 경우가 있다.