안경잡이개발자

728x90
반응형

  흔히 웹 서버 프로그램을 개발할 때 가장 중요한 것은 보안입니다. 저는 개발을 할 때 보안성을 강화하는 모듈화 방법에 대해서 많이 고민합니다. 예를 들어 특정한 소스코드를 깃 허브(Git Hub)에 올리고 있는 상황에서, 데이터베이스 연결 구문 등을 작성하고자 할 때 데이터베이스 연동 구문에는 데이터베이스의 ID와 비밀번호가 그대로 노출이 될 텐데 그걸 그대로 깃 허브로 푸시(Push) 하지는 않겠죠? 이럴 때 사용하는 것이 바로 파일에서부터 데이터베이스 관련 정보를 불러오도록 하는 것입니다.


  파일에서부터 데이터베이스 관련 정보를 불러오도록 하면 웹 쉘 업로드 공격이 들어오지 않는 이상 데이터베이스 관련 정보를 노출당할 일이 없고, 데이터베이스 또한 적절하게 권한 분리를 함으로써 웹 서버에서 접근할 수 있는 데이터베이스 영역을 잘 관리한다면 상당히 보안성이 높아집니다. (물론 웹 서버를 Root 권한으로 돌리거나, 데이터베이스 또한 Root 권한으로 접속하도록 개발된 서버라면 웹 쉘 한 방에 초전 박살날 수 있습니다.)


  아무튼 카페 24와 같은 호스팅 서비스를 이용할 때 파일에서부터 데이터베이스 관련 정보를 읽어 처리하는 방법에 대해 알려드리고자 합니다.



  먼저 위와 같이 현재의 컨텍스트 경로를 출력하도록 하는 JSP 프로그램을 작성해서 호스팅 서버에 올립니다.


  실행 결과 위와 같이 리눅스 경로가 출력되는 것을 알 수 있어요. 제 루트 디렉토리는 바로 위와 같다고 할 수 있습니다.



  이제 한번 루트 디렉토리의 바로 부모 폴더로 가서 file이라는 폴더를 만들어줍니다. 이제 정상적인 사용자들이 이 경로에 웹 브라우저로 접근할 수 없습니다.



  이제 위와 같이 특정한 텍스트 파일에 아이디 값을 적어서 올려볼게요.


  이제 바로 위와 같이 아까 확인했던 경로 값을 이용해 해당 파일에서 글자를 읽어 출력하도록 해봅시다.



  바로 위와 같이 성공적으로 아이디 값을 읽어온 것을 알 수 있습니다. 그럼 즐거운 시큐어 코딩 되세요!


728x90
반응형

Comment +0

728x90
반응형

  일반적으로 JSP, PHP 등을 이용해서 Google Gmail STMP 서비스를 이용할 수 있습니다. 일반적으로 구글에 회원가입을 한 뒤에 핸드폰으로 인증을 하게 되면 지메일 서비스를 바로 사용할 수 있는데요, 그렇게 지메일을 사용할 수 있는 상태에서는 해당 지메일 계정을 우리의 웹 서버에서 사용할 수 있는 거죠. 일반적으로 회원가입 이후에 이메일 인증 메일을 보내거나 할 때 우리의 웹 사이트에 넣을 수 있는 모듈입니다. 찾아보시면 금방 소스코드를 구해서 구현하시는 건 어렵지 않을 건데요, 카페 24와 같은 호스팅 서비스에 이를 적용하고자 하면 문제가 발생합니다.


  하지만 호스팅 서비스를 이용하는 경우에는 콘솔(Console) 창을 직접 확인하기 어려워 정확히 어떤 오류가 발생하는지 알기 힘들죠. 저 같은 경우에도 단순히 로컬 호스트에서는 잘 돌아가던 SMTP 모듈이 카페 24 호스팅 서버에 직접 업로드하니까 오류가 발생했다고만 나오더라구요.


  카페 24(Cafe 24)에서 JSP 호스팅을 신청하면, 퓨티(Putty)와 같은 SSH 접속 프로그램을 이용해 자신이 구매한 JSP 톰캣 서버에 접속할 수 있습니다. 그 중에서도 웹 서버에 오류가 발생하는 등 로그를 확인해야 할 때는 다음과 같이 할 수 있어요.


  ▶ cd tomcat/logs/ : 톰캣의 로그 폴더로 이동합니다.

  ▶ tail -f catalina.out : 현 시점에서부터 발생하는 웹 로그를 모두 화면에 출력합니다.



  위와 같이 웹 로그 출력이 돌아가도록 만든 이후에 확인해봅시다.



  마찬가지로 다시 SMTP 오류가 나는 부분의 웹 페이지에 접속합니다. 그러면 이번에는 발생하는 오류 메시지가 위와 같이 콘솔 창에 출력되는 것을 알 수 있습니다. 대충 보니까 카페 24에서 구글 SMTP로 접속하는 과정에서 인증 오류가 발생한 것을 알 수 있습니다. 이것은 호스팅 서비스를 경유하기 때문이에요. 기존에 로컬 호스트를 사용하던 때보다 구글 측에서 자원 고갈 공격 등을 받을 여지가 있어서, 추가적인 보안 인증을 거쳐야지 지메일을 이용할 수 있도록 구글 측에서 규제를 넣은 것이랍니다. 따라서 바로 추가 인증 과정을 통해 SMTP 서비스를 사용해보도록 하겠습니다.



  바로 위와 같이 구글 서비스에서 '계정' 탭에 들어가도록 합니다. 이후에 '로그인 및 보안' 탭에 들어가시면 됩니다.



  그리고 위와 같이 '로그인 및 보안'에서 2단계 인증을 사용하도록 처리하시면 됩니다. 현재는 '사용 중지' 상태네요.



  이제 위와 같이 2단계 인증을 위해서 핸드폰 인증을 해주시면 됩니다. '시작하기' 버튼을 눌러줄게요.



  먼저 위와 같이 비밀번호를 입력하라고 할 거에요.


  이후에 위와 같이 휴대폰 인증을 해주시면 됩니다. 별 거 없어요. 핸드폰으로 문자 한 통 오는데 그 인증번호 입력해주시면 됩니다.



  휴대폰 인증이 완료되면 위와 같이 '사용 설정'을 해줄 수 있습니다.



  이제 위와 같이 다시 '로그인 및 보안' 탭에 들어가면 '앱 비밀번호' 부분이 있습니다. 이를 생성해주시면 됩니다.



  이제 위와 같이 '기타'로 하나의 비밀번호를 새롭게 추가해주시면 됩니다.



  기본적으로 위와 같이 서비스의 이름을 입력하면 알아서 비밀번호가 나온답니다.



  이제 위와 같이 비밀번호가 등장했네요. 



  위와 같이 성공적으로 서비스가 생성되었구요. 이제 SMTP 소스코드에서 인증하는 부분의 비밀번호를 아까 받은 비밀번호로 넣으시면 됩니다. 아이디는 그대로 구글 계정을 쓰시면 되구요. JSP에서의 예제는 다음과 같습니다.


package util;


import javax.mail.Authenticator;

import javax.mail.PasswordAuthentication;


public class Gmail extends Authenticator {


    @Override

    protected PasswordAuthentication getPasswordAuthentication() {

        return new PasswordAuthentication("구글 이메일 계정","부여받은 비밀번호");

    }

    

}


  이제 위와 같이 톰캣 서버를 재시작해주면 됩니다.


  ▶ CD ~ : 다시 홈 디렉토리로 이동하기

  ▶ ./tomcat/bin/shutdown.sh : 현재 실행 중인 톰캣을 종료시키기

  ▶ ./tomcat/bin/startup.sh : 톰캣을 다시 실행시키기



이제 한 번 테스트를 해보겠습니다.



  위와 같이 성공적으로 오류 없이 메일이 전송되는 것을 확인할 수 있습니다.

728x90
반응형

Comment +2

728x90
반응형

  이제 JSP로 강의평가(Lecture Evaluation) 웹 사이트 개발하기의 열 한 번째 포스팅을 합니다. 사실상 지난 시간에 모든 기능들이 다 구현이 되었어요. 이제는 기본적인 프로젝트 뼈대가 완성되었으므로 다양한 기능을 붙이거나, 보안적인 요소를 처리하는 것이 좋겠지요. 저는 이번 시간에서는 간단히 기초 시큐어 코딩 방법에 대해서 소개해드리려고 합니다. XSS를 방지하는 겁니다. XSS는 기본적으로 특정 사용자가 다른 사용자와 통신하는 과정에서 발생할 수 있는 해킹 방법입니다. 대표적으로는, 강의 평가 글 자체가 될 수 있습니다. 따라서 강의 평가 글을 출력할 때는 자바스크립트를 실행할 수 있는 문장을 모두 치환하면 됩니다.


  ▶ EvaluationDAO.java 내부의 write() 함수 수정하기


public int write(EvaluationDTO evaluationDTO) {

PreparedStatement pstmt = null;

try {

String SQL = "INSERT INTO EVALUATION VALUES (NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0);";

pstmt = conn.prepareStatement(SQL);

pstmt.setString(1, evaluationDTO.getUserID().replaceAll("<", "&lt;").replaceAll(">", " &gt;").replaceAll("\r\n", "<br>"));

pstmt.setString(2, evaluationDTO.getLectureName().replaceAll("<", "&lt;").replaceAll(">", " &gt;").replaceAll("\r\n", "<br>"));

pstmt.setString(3, evaluationDTO.getProfessorName().replaceAll("<", "&lt;").replaceAll(">", " &gt;").replaceAll("\r\n", "<br>"));

pstmt.setInt(4, evaluationDTO.getLectureYear());

pstmt.setString(5, evaluationDTO.getSemesterDivide().replaceAll("<", "&lt;").replaceAll(">", " &gt;").replaceAll("\r\n", "<br>"));

pstmt.setString(6, evaluationDTO.getLectureDivide().replaceAll("<", "&lt;").replaceAll(">", " &gt;").replaceAll("\r\n", "<br>"));

pstmt.setString(7, evaluationDTO.getEvaluationTitle().replaceAll("<", "&lt;").replaceAll(">", " &gt;").replaceAll("\r\n", "<br>"));

pstmt.setString(8, evaluationDTO.getEvaluationContent().replaceAll("<", "&lt;").replaceAll(">", " &gt;").replaceAll("\r\n", "<br>"));

pstmt.setString(9, evaluationDTO.getTotalScore().replaceAll("<", "&lt;").replaceAll(">", " &gt;").replaceAll("\r\n", "<br>"));

pstmt.setString(10, evaluationDTO.getCreditScore().replaceAll("<", "&lt;").replaceAll(">", " &gt;").replaceAll("\r\n", "<br>"));

pstmt.setString(11, evaluationDTO.getComfortableScore().replaceAll("<", "&lt;").replaceAll(">", " &gt;").replaceAll("\r\n", "<br>"));

pstmt.setString(12, evaluationDTO.getLectureScore().replaceAll("<", "&lt;").replaceAll(">", " &gt;").replaceAll("\r\n", "<br>"));

return pstmt.executeUpdate();

} catch (Exception e) {

e.printStackTrace();

} finally {

try {

if(pstmt != null) pstmt.close();

if(conn != null) conn.close();

} catch (Exception e) {

e.printStackTrace();

}

}

return -1;

}


  위와 같이 설정해주면 게시글이 등록될 때 모든 꺾쇠가 치환되므로 기본적으로 자바스크립트 구문을 삽입하기가 어려워진답니다. 이미 시중에 나와있는 라이브러리가 많긴 한데, 대충 위와 같이 처리해도 XSS 공격이 까다로워지므로 나쁘지 않습니다. 테스트 결과는 다음과 같습니다.




  ※ 전체 실행 결과 (동영상 결과물) ※



728x90
반응형

Comment +0

728x90
반응형

  이번 시간에는 JSP로 강의평가(Lecture Evaluation) 웹 사이트 개발하기의 열 번째 강의입니다. 지난 시간에는 특정한 강의를 검색할 수 있고, 페이징 처리를 하여 페이지를 넘기면서 여러 강의를 볼 수 있도록 기능을 구현하는 시간을 가졌습니다. 이번 시간에는 특정한 강의를 추천하고, 자신이 작성한 글에 한해서 글을 삭제하는 기능을 개발하고자 합니다. 바로 시작해봅시다. 가장 먼저 지난 번에 데이터베이스를 설계하는 시간에 작성했었던 LIKEY 테이블을 조금 바꿔주도록 하겠습니다. 바로 다음과 같이 ALTER 명령어를 이용해서 (회원 아이디, 평가 번호)로 PRIMARY KEY를 줍시다. 왜냐하면 한 명은 특정한 글에 단 한 번만 추천을 누를 수 있어야 하기 때문입니다.


  ▶ ALTER TABLE LIKEY ADD PRIMARY KEY (userID, evaluationID);



  이후에 바로 EvaluationDAO.java 파일을 수정하도록 하겠습니다. 이번 시간에 구현할 기능을 위해 몇 가지 데이터베이스 접근 함수가 필요하기 때문이에요.


public int like(String evaluationID) {

PreparedStatement pstmt = null;

try {

String SQL = "UPDATE EVALUATION SET likeCount = likeCount + 1 WHERE evaluationID = ?";

pstmt = conn.prepareStatement(SQL);

pstmt.setInt(1, Integer.parseInt(evaluationID));

return pstmt.executeUpdate();

} catch (Exception e) {

e.printStackTrace();

} finally {

try {

if(pstmt != null) pstmt.close();

if(conn != null) conn.close();

} catch (Exception e) {

e.printStackTrace();

}

}

return -1;

}

public int delete(String evaluationID) {

PreparedStatement pstmt = null;

try {

String SQL = "DELETE FROM EVALUATION WHERE evaluationID = ?";

pstmt = conn.prepareStatement(SQL);

pstmt.setInt(1, Integer.parseInt(evaluationID));

return pstmt.executeUpdate();

} catch (Exception e) {

e.printStackTrace();

} finally {

try {

if(pstmt != null) pstmt.close();

if(conn != null) conn.close();

} catch (Exception e) {

e.printStackTrace();

}

}

return -1;

}

public String getUserID(String evaluationID) {

PreparedStatement pstmt = null;

try {

String SQL = "SELECT userID FROM EVALUATION WHERE evaluationID = ?";

pstmt = conn.prepareStatement(SQL);

pstmt.setInt(1, Integer.parseInt(evaluationID));

rs = pstmt.executeQuery();

while(rs.next()) {

return rs.getString(1);

}

} catch (Exception e) {

e.printStackTrace();

} finally {

try {

if(pstmt != null) pstmt.close();

if(conn != null) conn.close();

} catch (Exception e) {

e.printStackTrace();

}

}

return null;

}


  위와 같이 총 3개의 함수를 추가해줍시다. 차례대로 추천을 눌러서 특정한 글의 추천 갯수가 증가되는 함수, 특정한 강의 평가 글을 지우는 함수, 특정한 강의 평가 글을 작성한 사용자의 아이디를 구하는 함수입니다. 이제 다음과 같이 우리의 프로젝트를 구성합니다. likey 패키지를 생성해서 LikeyDTO.java, LikeyDAO.java를 생성합니다. 그리고 deleteAction.jsp 페이지와 likeAction.jsp 페이지를 생성합니다. 이 두 페이지는 강의 평가 글을 지우는 액션 페이지, 강의 평가에 추천을 누르는 액션 페이지입니다.



  ▶ LikeyDTO.java


package likey;


public class LikeyDTO {


String userID;

int evaluationID;

String userIP;

public String getUserID() {

return userID;

}

public void setUserID(String userID) {

this.userID = userID;

}

public int getEvaluationID() {

return evaluationID;

}

public void setEvaluationID(int evaluationID) {

this.evaluationID = evaluationID;

}

public String getUserIP() {

return userIP;

}

public void setUserIP(String userIP) {

this.userIP = userIP;

}

public LikeyDTO(String userID, int evaluationID, String userIP) {

this.userID = userID;

this.evaluationID = evaluationID;

this.userIP = userIP;

}


}


  ▶ LikeyDAO.java


package likey;


import java.sql.Connection;

import java.sql.DriverManager;

import java.sql.PreparedStatement;

import java.sql.SQLException;


public class LikeyDAO {


private Connection conn;


public LikeyDAO() {

try {

String dbURL = "jdbc:mysql://localhost:3306/LectureEvaluation";

String dbID = "root";

String dbPassword = "root1234";

Class.forName("com.mysql.jdbc.Driver");

conn = DriverManager.getConnection(dbURL, dbID, dbPassword);

} catch (Exception e) {

e.printStackTrace();

}

}

public int like(String userID, String evaluationID, String userIP) {

String SQL = "INSERT INTO LIKEY VALUES (?, ?, ?)";

try {

PreparedStatement pstmt = conn.prepareStatement(SQL);

pstmt.setString(1, userID);

pstmt.setString(2, evaluationID);

pstmt.setString(3, userIP);

return pstmt.executeUpdate();

} catch (SQLException e) {

e.printStackTrace();

}

return -1; // 추천 중복 오류

}

}


  ▶ deleteAction.jsp


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

<%@ page import="user.UserDAO"%>

<%@ page import="evaluation.EvaluationDAO"%>

<%@ page import="likey.LikeyDTO"%>

<%@ page import="java.io.PrintWriter"%>

<%

String userID = null;

if(session.getAttribute("userID") != null) {

userID = (String) session.getAttribute("userID");

}

if(userID == null) {

PrintWriter script = response.getWriter();

script.println("<script>");

script.println("alert('로그인을 해주세요.');");

script.println("location.href = 'userLogin.jsp'");

script.println("</script>");

script.close();

return;

}

request.setCharacterEncoding("UTF-8");

String evaluationID = null;

if(request.getParameter("evaluationID") != null) {

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

}

EvaluationDAO evaluationDAO = new EvaluationDAO();

if(userID.equals(evaluationDAO.getUserID(evaluationID))) {

int result = new EvaluationDAO().delete(evaluationID);

if (result == 1) {

session.setAttribute("userID", userID);

PrintWriter script = response.getWriter();

script.println("<script>");

script.println("alert('삭제가 완료되었습니다.');");

script.println("location.href='index.jsp'");

script.println("</script>");

script.close();

return;

} else {

PrintWriter script = response.getWriter();

script.println("<script>");

script.println("alert('데이터베이스 오류가 발생했습니다.');");

script.println("history.back();");

script.println("</script>");

script.close();

return;

}

} else {

PrintWriter script = response.getWriter();

script.println("<script>");

script.println("alert('자신이 쓴 글만 삭제 가능합니다.');");

script.println("history.back();");

script.println("</script>");

script.close();

return;

}

%>


  ▶ likeAction.jsp


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

<%@ page import="user.UserDAO"%>

<%@ page import="evaluation.EvaluationDAO"%>

<%@ page import="likey.LikeyDAO"%>

<%@ page import="java.io.PrintWriter"%>

<%!

public static String getClientIP(HttpServletRequest request) {

    String ip = request.getHeader("X-FORWARDED-FOR"); 

    if (ip == null || ip.length() == 0) {

        ip = request.getHeader("Proxy-Client-IP");

    }

    if (ip == null || ip.length() == 0) {

        ip = request.getHeader("WL-Proxy-Client-IP");

    }

    if (ip == null || ip.length() == 0) {

        ip = request.getRemoteAddr() ;

    }

    return ip;

}

%>

<%

String userID = null;

if(session.getAttribute("userID") != null) {

userID = (String) session.getAttribute("userID");

}

if(userID == null) {

PrintWriter script = response.getWriter();

script.println("<script>");

script.println("alert('로그인을 해주세요.');");

script.println("location.href = 'userLogin.jsp'");

script.println("</script>");

script.close();

return;

}

request.setCharacterEncoding("UTF-8");

String evaluationID = null;

if(request.getParameter("evaluationID") != null) {

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

}

EvaluationDAO evaluationDAO = new EvaluationDAO();

LikeyDAO likeyDAO = new LikeyDAO();

int result = likeyDAO.like(userID, evaluationID, getClientIP(request));

if (result == 1) {

result = evaluationDAO.like(evaluationID);

if (result == 1) {

PrintWriter script = response.getWriter();

script.println("<script>");

script.println("alert('추천이 완료되었습니다.');");

script.println("location.href='index.jsp'");

script.println("</script>");

script.close();

return;

} else {

PrintWriter script = response.getWriter();

script.println("<script>");

script.println("alert('데이터베이스 오류가 발생했습니다.');");

script.println("history.back();");

script.println("</script>");

script.close();

return;

}

} else {

PrintWriter script = response.getWriter();

script.println("<script>");

script.println("alert('이미 추천을 누른 글입니다.');");

script.println("history.back();");

script.println("</script>");

script.close();

return;

}

%>


  이제 한 번 테스트를 해보도록 합시다.







728x90
반응형

Comment +0

728x90
반응형

  이번 시간에는 JSP 강의평가(Lecture Evaluation) 웹 사이트 개발하기의 아홉 번째 시간입니다. 이번 시간에는 등록된 강의 평가 글을 실질적으로 출력하고, 검색할 수 있는 기능을 구현해보도록 하겠습니다.


  ▶ EvaluationDAO.java에 검색 함수 추가하기


public ArrayList<EvaluationDTO> getList(String lectureDivide, String searchType, String search, int pageNumber) {

if(lectureDivide.equals("전체")) {

lectureDivide = "";

}

ArrayList<EvaluationDTO> evaluationList = null;

PreparedStatement pstmt = null;

String SQL = "";

try {

if(searchType.equals("최신순")) {

SQL = "SELECT * FROM EVALUATION WHERE lectureDivide LIKE ? AND CONCAT(lectureName, professorName, evaluationTitle, evaluationContent) LIKE ? ORDER BY evaluationID DESC LIMIT " + pageNumber * 5 + ", " + pageNumber * 5 + 6;

} else if(searchType.equals("추천순")) {

SQL = "SELECT * FROM EVALUATION WHERE lectureDivide LIKE ? AND CONCAT(lectureName, professorName, evaluationTitle, evaluationContent) LIKE ? ORDER BY likeCount DESC LIMIT " + pageNumber * 5 + ", " + pageNumber * 5 + 6;

}

pstmt = conn.prepareStatement(SQL);

pstmt.setString(1, "%" + lectureDivide + "%");

pstmt.setString(2, "%" + search + "%");

rs = pstmt.executeQuery();

evaluationList = new ArrayList<EvaluationDTO>();

while(rs.next()) {

EvaluationDTO evaluation = new EvaluationDTO(

rs.getInt(1),

rs.getString(2),

rs.getString(3),

rs.getString(4),

rs.getInt(5),

rs.getString(6),

rs.getString(7),

rs.getString(8),

rs.getString(9),

rs.getString(10),

rs.getString(11),

rs.getString(12),

rs.getString(13),

rs.getInt(14)

);

evaluationList.add(evaluation);

}

} catch (Exception e) {

e.printStackTrace();

} finally {

try {

if(rs != null) rs.close();

if(pstmt != null) pstmt.close();

if(conn != null) conn.close();

} catch (Exception e) {

e.printStackTrace();

}

}

return evaluationList;

}


    기본적으로 사용자가 '최신순', '추천순'으로 검색하는 쿼리에 따라서 정렬을 다르게 합니다. 이후에 한 페이지 당 5개씩 평가 글을 출력하도록 위와 같이 구성해보았습니다. 이제 다음과 같이 index.jsp를 수정합시다.


  ▶ index.jsp 수정하기


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

<%@ page import="java.io.PrintWriter"%>

<%@ page import="user.UserDAO"%>

<%@ page import="evaluation.EvaluationDAO"%>

<%@ page import="evaluation.EvaluationDTO"%>

<%@ page import="java.util.ArrayList"%>

<%@ page import="java.net.URLEncoder"%>

<!doctype html>

<html>

  <head>

    <title>강의평가 웹 사이트</title>

    <meta charset="utf-8">

    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- 부트스트랩 CSS 추가하기 -->

    <link rel="stylesheet" href="./css/bootstrap.min.css">

    <!-- 커스텀 CSS 추가하기 -->

    <link rel="stylesheet" href="./css/custom.css">

  </head>

  <body>

<%

request.setCharacterEncoding("UTF-8");

String lectureDivide = "전체";

String searchType = "최신순";

String search = "";

int pageNumber = 0;

if(request.getParameter("lectureDivide") != null) {

lectureDivide = request.getParameter("lectureDivide");

}

if(request.getParameter("searchType") != null) {

searchType = request.getParameter("searchType");

}

if(request.getParameter("search") != null) {

search = request.getParameter("search");

}

if(request.getParameter("pageNumber") != null) {

try {

pageNumber = Integer.parseInt(request.getParameter("pageNumber"));

} catch (Exception e) {

System.out.println("검색 페이지 번호 오류");

}

}

String userID = null;

if(session.getAttribute("userID") != null) {

userID = (String) session.getAttribute("userID");

}

if(userID == null) {

PrintWriter script = response.getWriter();

script.println("<script>");

script.println("alert('로그인을 해주세요.');");

script.println("location.href = 'userLogin.jsp'");

script.println("</script>");

script.close();

}

boolean emailChecked = new UserDAO().getUserEmailChecked(userID);

if(emailChecked == false) {

PrintWriter script = response.getWriter();

script.println("<script>");

script.println("location.href = 'emailSendConfirm.jsp'");

script.println("</script>");

script.close();

return;

}

%>

    <nav class="navbar navbar-expand-lg navbar-light bg-light">

      <a class="navbar-brand" href="index.jsp">강의평가 웹 사이트</a>

      <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar">

        <span class="navbar-toggler-icon"></span>

      </button>

      <div class="collapse navbar-collapse" id="navbar">

        <ul class="navbar-nav mr-auto">

          <li class="nav-item active">

            <a class="nav-link" href="index.jsp">메인</a>

          </li>

          <li class="nav-item dropdown">

            <a class="nav-link dropdown-toggle" id="dropdown" data-toggle="dropdown">

              회원 관리

            </a>

            <div class="dropdown-menu" aria-labelledby="dropdown">

<%

if(userID == null) {

%>

              <a class="dropdown-item" href="userLogin.jsp">로그인</a>

              <a class="dropdown-item" href="userRegister.jsp">회원가입</a>

<%

} else {

%>

              <a class="dropdown-item" href="userLogout.jsp">로그아웃</a>

<%

}

%>

            </div>

          </li>

        </ul>

        <form action="./index.jsp" method="get" class="form-inline my-2 my-lg-0">

          <input type="text" name="search" class="form-control mr-sm-2" placeholder="내용을 입력하세요.">

          <button class="btn btn-outline-success my-2 my-sm-0" type="submit">검색</button>

        </form>

      </div>

    </nav>

    <div class="container">

      <form method="get" action="./index.jsp" class="form-inline mt-3">

        <select name="lectureDivide" class="form-control mx-1 mt-2">

          <option value="전체">전체</option>

          <option value="전공" <%if(lectureDivide.equals("전공")) out.println("selected");%>>전공</option>

          <option value="교양" <%if(lectureDivide.equals("교양")) out.println("selected");%>>교양</option>

          <option value="기타" <%if(lectureDivide.equals("기타")) out.println("selected");%>>기타</option>

        </select>

        <select name="searchType" class="form-control mx-1 mt-2">

          <option value="최신순">최신순</option>

          <option value="추천순" <%if(searchType.equals("추천순")) out.println("selected");%>>추천순</option>

        </select>

        <input type="text" name="search" class="form-control mx-1 mt-2" value="<%= search %>" placeholder="내용을 입력하세요.">

        <button type="submit" class="btn btn-primary mx-1 mt-2">검색</button>

        <a class="btn btn-primary mx-1 mt-2" data-toggle="modal" href="#registerModal">등록하기</a>

        <a class="btn btn-danger ml-1 mt-2" data-toggle="modal" href="#reportModal">신고</a>

      </form>

<%

ArrayList<EvaluationDTO> evaluationList = new ArrayList<EvaluationDTO>();

evaluationList = new EvaluationDAO().getList(lectureDivide, searchType, search, pageNumber);

if(evaluationList != null)

for(int i = 0; i < evaluationList.size(); i++) {

if(i == 5) break;

EvaluationDTO evaluation = evaluationList.get(i);

%>

      <div class="card bg-light mt-3">

        <div class="card-header bg-light">

          <div class="row">

            <div class="col-8 text-left"><%=evaluation.getLectureName()%>&nbsp;<small><%=evaluation.getProfessorName()%></small></div>

            <div class="col-4 text-right">

              종합 <span style="color: red;"><%=evaluation.getTotalScore()%></span>

            </div>

          </div>

        </div>

        <div class="card-body">

          <h5 class="card-title">

            <%=evaluation.getEvaluationTitle()%>&nbsp;<small>(<%=evaluation.getLectureYear()%>년 <%=evaluation.getSemesterDivide()%>)</small>

          </h5>

          <p class="card-text"><%=evaluation.getEvaluationContent()%></p>

          <div class="row">

            <div class="col-9 text-left">

              성적 <span style="color: red;"><%=evaluation.getCreditScore()%></span>

              널널 <span style="color: red;"><%=evaluation.getComfortableScore()%></span>

              강의 <span style="color: red;"><%=evaluation.getLectureScore()%></span>

              <span style="color: green;">(추천: <%=evaluation.getLikeCount()%>)</span>

            </div>

            <div class="col-3 text-right">

              <a onclick="return confirm('추천하시겠습니까?')" href="./likeAction.jsp?evaluationID=<%=evaluation.getEvaluationID()%>">추천</a>

              <a onclick="return confirm('삭제하시겠습니까?')" href="./deleteAction.jsp?evaluationID=<%=evaluation.getEvaluationID()%>">삭제</a>

            </div>

          </div>

        </div>

      </div>

<%

}

%>

    </div>

    <ul class="pagination justify-content-center mt-3">

      <li class="page-item">

<%

if(pageNumber <= 0) {

%>     

        <a class="page-link disabled">이전</a>

<%

} else {

%>

<a class="page-link" href="./index.jsp?lectureDivide=<%=URLEncoder.encode(lectureDivide, "UTF-8")%>&searchType=<%=URLEncoder.encode(searchType, "UTF-8")%>&search=<%=URLEncoder.encode(search, "UTF-8")%>&pageNumber=<%=pageNumber - 1%>">이전</a>

<%

}

%>

      </li>

      <li class="page-item">

<%

if(evaluationList.size() < 6) {

%>     

        <a class="page-link disabled">다음</a>

<%

} else {

%>

<a class="page-link" href="./index.jsp?lectureDivide=<%=URLEncoder.encode(lectureDivide, "UTF-8")%>&searchType=<%=URLEncoder.encode(searchType, "UTF-8")%>&search=<%=URLEncoder.encode(search, "UTF-8")%>&pageNumber=<%=pageNumber + 1%>">다음</a>

<%

}

%>

      </li>

    </ul>

    <div class="modal fade" id="registerModal" tabindex="-1" role="dialog" aria-labelledby="modal" aria-hidden="true">

      <div class="modal-dialog">

        <div class="modal-content">

          <div class="modal-header">

            <h5 class="modal-title" id="modal">평가 등록</h5>

            <button type="button" class="close" data-dismiss="modal" aria-label="Close">

              <span aria-hidden="true">&times;</span>

            </button>

          </div>

          <div class="modal-body">

            <form action="./evaluationRegisterAction.jsp" method="post">

              <div class="form-row">

                <div class="form-group col-sm-6">

                  <label>강의명</label>

                  <input type="text" name="lectureName" class="form-control" maxlength="20">

                </div>

                <div class="form-group col-sm-6">

                  <label>교수명</label>

                  <input type="text" name="professorName" class="form-control" maxlength="20">

                </div>

              </div>

              <div class="form-row">

                <div class="form-group col-sm-4">

                  <label>수강 년도</label>

                  <select name="lectureYear" class="form-control">

                    <option value="2011">2011</option>

                    <option value="2012">2012</option>

                    <option value="2013">2013</option>

                    <option value="2014">2014</option>

                    <option value="2015">2015</option>

                    <option value="2016">2016</option>

                    <option value="2017">2017</option>

                    <option value="2018" selected>2018</option>

                    <option value="2019">2019</option>

                    <option value="2020">2020</option>

                    <option value="2021">2021</option>

                    <option value="2022">2022</option>

                    <option value="2023">2023</option>

                  </select>

                </div>

                <div class="form-group col-sm-4">

                  <label>수강 학기</label>

                  <select name="semesterDivide" class="form-control">

                    <option name="1학기" selected>1학기</option>

                    <option name="여름학기">여름학기</option>

                    <option name="2학기">2학기</option>

                    <option name="겨울학기">겨울학기</option>

                  </select>

                </div>

                <div class="form-group col-sm-4">

                  <label>강의 구분</label>

                  <select name="lectureDivide" class="form-control">

                    <option name="전공" selected>전공</option>

                    <option name="교양">교양</option>

                    <option name="기타">기타</option>

                  </select>

                </div>

              </div>

              <div class="form-group">

                <label>제목</label>

                <input type="text" name="evaluationTitle" class="form-control" maxlength="20">

              </div>

              <div class="form-group">

                <label>내용</label>

                <textarea type="text" name="evaluationContent" class="form-control" maxlength="2048" style="height: 180px;"></textarea>

              </div>

              <div class="form-row">

                <div class="form-group col-sm-3">

                  <label>종합</label>

                  <select name="totalScore" class="form-control">

                    <option value="A" selected>A</option>

                    <option value="B">B</option>

                    <option value="C">C</option>

                    <option value="D">D</option>

                    <option value="F">F</option>

                  </select>

                </div>

                <div class="form-group col-sm-3">

                  <label>성적</label>

                  <select name="creditScore" class="form-control">

                    <option value="A" selected>A</option>

                    <option value="B">B</option>

                    <option value="C">C</option>

                    <option value="D">D</option>

                    <option value="F">F</option>

                  </select>

                </div>

                <div class="form-group col-sm-3">

                  <label>널널</label>

                  <select name="comfortableScore" class="form-control">

                    <option value="A" selected>A</option>

                    <option value="B">B</option>

                    <option value="C">C</option>

                    <option value="D">D</option>

                    <option value="F">F</option>

                  </select>

                </div>

                <div class="form-group col-sm-3">

                  <label>강의</label>

                  <select name="lectureScore" class="form-control">

                    <option value="A" selected>A</option>

                    <option value="B">B</option>

                    <option value="C">C</option>

                    <option value="D">D</option>

                    <option value="F">F</option>

                  </select>

                </div>

              </div>

              <div class="modal-footer">

                <button type="button" class="btn btn-secondary" data-dismiss="modal">취소</button>

                <button type="submit" class="btn btn-primary">등록하기</button>

              </div>

            </form>

          </div>

        </div>

      </div>

    </div>

    <div class="modal fade" id="reportModal" tabindex="-1" role="dialog" aria-labelledby="modal" aria-hidden="true">

      <div class="modal-dialog">

        <div class="modal-content">

          <div class="modal-header">

            <h5 class="modal-title" id="modal">신고하기</h5>

            <button type="button" class="close" data-dismiss="modal" aria-label="Close">

              <span aria-hidden="true">&times;</span>

            </button>

          </div>

          <div class="modal-body">

            <form method="post" action="./reportAction.jsp">

              <div class="form-group">

                <label>신고 제목</label>

                <input type="text" name="reportTitle" class="form-control" maxlength="20">

              </div>

              <div class="form-group">

                <label>신고 내용</label>

                <textarea type="text" name="reportContent" class="form-control" maxlength="2048" style="height: 180px;"></textarea>

              </div>

              <div class="modal-footer">

                <button type="button" class="btn btn-secondary" data-dismiss="modal">취소</button>

                <button type="submit" class="btn btn-danger">신고하기</button>

              </div>

            </form>

          </div>

        </div>

      </div>

    </div>

    <footer class="bg-dark mt-4 p-5 text-center" style="color: #FFFFFF;">

      Copyright ⓒ 2018 나동빈 All Rights Reserved.

    </footer>

    <!-- 제이쿼리 자바스크립트 추가하기 -->

    <script src="./js/jquery.min.js"></script>

    <!-- Popper 자바스크립트 추가하기 -->

    <script src="./js/popper.min.js"></script>

    <!-- 부트스트랩 자바스크립트 추가하기 -->

    <script src="./js/bootstrap.min.js"></script>

  </body>

</html>


  이제 한 번 실행해서 테스트를 해보도록 하겠습니다.






728x90
반응형

Comment +4

  • 안경벗은개발자 2018.03.28 14:40

    위에 올려놓은 소스에 문제가 있습니다.

    1. textarea 는 type으로 text를 따로 명시하면 안됩니다.
    2. option은 value로 지정해야합니다. name으로 주면 안됩니다.
    3. connection에 대해 초기화 해주지않았습니다.
    4. connection conn에 util패키지 하위에 getConnection을 해줘야 합니다.

    • 제가 몰랐던 내용을 말씀해주셔서 감사합니다! 글은 수정이 가능한데, 유튜브에 올려 놓은 동영상 강좌는 수정이 불가능해 마음이 아프네요...

    • 안경벗은개발자 2018.04.17 09:59

      항상 응원합니다.
      유투브등으로 많은 도움이되어,
      그간의 지식에 대한 보상으로 유데미 유료결제또한 해드렸구요.
      학업으로 많이 바쁘신거같군요..
      아직 메세지 응답이나, 메일을 확인하지 못하는듯 한데.. 힘내세요

  • ji 2019.12.03 23:19

    관리자의 승인을 기다리고 있는 댓글입니다

728x90
반응형

  이번 시간에는 JSP 강의평가(Lecture Evaluation) 웹 사이트 개발하기의 여덟 번째 시간입니다. 지난 시간에는 로그인 및 로그아웃 기능을 구현했습니다. 이제 로그인 이후에 사용자들이 강의 평가 글을 등록하는 기능을 작성해보도록 하겠습니다. 또한 특정한 게시글 등에 대해서 신고를 하는 기능 또한 구현을 하려고 합니다. 바로 시작을 해보도록 합시다.



  위와 같이 두 개의 JSP 페이지를 추가합니다. 하나는 reportAction.jsp이고, 다른 하나는 evaluationRegisterAction.jsp입니다. 그리고 evaluation 패키지를 생성합니다. 이후에 해당 패키지 안에 EvaluationDTO.java, EvaluationDAO.java를 작성해주시면 됩니다.


  ▶ EvaluationDTO.java: 하나의 강의 평가 정보를 담고 있는 데이터베이스 트랜잭션 객체 역할을 담당합니다.


package evaluation;


public class EvaluationDTO {


int evaluationID;

String userID;

String lectureName;

String professorName;

int lectureYear;

String semesterDivide;

String lectureDivide;

String evaluationTitle;

String evaluationContent;

String totalScore;

String creditScore;

String comfortableScore;

String lectureScore;

int likeCount;

public int getEvaluationID() {

return evaluationID;

}

public void setEvaluationID(int evaluationID) {

this.evaluationID = evaluationID;

}

public String getUserID() {

return userID;

}

public void setUserID(String userID) {

this.userID = userID;

}

public String getLectureName() {

return lectureName;

}

public void setLectureName(String lectureName) {

this.lectureName = lectureName;

}

public String getProfessorName() {

return professorName;

}

public void setProfessorName(String professorName) {

this.professorName = professorName;

}

public int getLectureYear() {

return lectureYear;

}

public void setLectureYear(int lectureYear) {

this.lectureYear = lectureYear;

}

public String getSemesterDivide() {

return semesterDivide;

}

public void setSemesterDivide(String semesterDivide) {

this.semesterDivide = semesterDivide;

}

public String getLectureDivide() {

return lectureDivide;

}

public void setLectureDivide(String lectureDivide) {

this.lectureDivide = lectureDivide;

}

public String getEvaluationTitle() {

return evaluationTitle;

}

public void setEvaluationTitle(String evaluationTitle) {

this.evaluationTitle = evaluationTitle;

}

public String getEvaluationContent() {

return evaluationContent;

}

public void setEvaluationContent(String evaluationContent) {

this.evaluationContent = evaluationContent;

}

public String getTotalScore() {

return totalScore;

}

public void setTotalScore(String totalScore) {

this.totalScore = totalScore;

}

public String getCreditScore() {

return creditScore;

}

public void setCreditScore(String creditScore) {

this.creditScore = creditScore;

}

public String getComfortableScore() {

return comfortableScore;

}

public void setComfortableScore(String comfortableScore) {

this.comfortableScore = comfortableScore;

}

public String getLectureScore() {

return lectureScore;

}

public void setLectureScore(String lectureScore) {

this.lectureScore = lectureScore;

}

public int getLikeCount() {

return likeCount;

}

public void setLikeCount(int likeCount) {

this.likeCount = likeCount;

}

public EvaluationDTO(int evaluationID, String userID, String lectureName, String professorName, int lectureYear,

String semesterDivide, String lectureDivide, String evaluationTitle, String evaluationContent,

String totalScore, String creditScore, String comfortableScore, String lectureScore, int likeCount) {

super();

this.evaluationID = evaluationID;

this.userID = userID;

this.lectureName = lectureName;

this.professorName = professorName;

this.lectureYear = lectureYear;

this.semesterDivide = semesterDivide;

this.lectureDivide = lectureDivide;

this.evaluationTitle = evaluationTitle;

this.evaluationContent = evaluationContent;

this.totalScore = totalScore;

this.creditScore = creditScore;

this.comfortableScore = comfortableScore;

this.lectureScore = lectureScore;

this.likeCount = likeCount;

}

}


  ▶ EvaluationDAO.java: 강의 평가와 관련된 데이터베이스 접근을 가능하게 해주는 객체입니다.


package evaluation;


import java.sql.Connection;

import java.sql.DriverManager;

import java.sql.PreparedStatement;

import java.sql.ResultSet;


public class EvaluationDAO {


private Connection conn;

private ResultSet rs;


public EvaluationDAO() {

try {

String dbURL = "jdbc:mysql://localhost:3306/LectureEvaluation";

String dbID = "데이터베이스 아이디";

String dbPassword = "데이터베이스 비밀번호";

Class.forName("com.mysql.jdbc.Driver");

conn = DriverManager.getConnection(dbURL, dbID, dbPassword);

} catch (Exception e) {

e.printStackTrace();

}

}

public int write(EvaluationDTO evaluationDTO) {

PreparedStatement pstmt = null;

try {

String SQL = "INSERT INTO EVALUATION VALUES (NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0);";

pstmt = conn.prepareStatement(SQL);

pstmt.setString(1, evaluationDTO.getUserID());

pstmt.setString(2, evaluationDTO.getLectureName());

pstmt.setString(3, evaluationDTO.getProfessorName());

pstmt.setInt(4, evaluationDTO.getLectureYear());

pstmt.setString(5, evaluationDTO.getSemesterDivide());

pstmt.setString(6, evaluationDTO.getLectureDivide());

pstmt.setString(7, evaluationDTO.getEvaluationTitle());

pstmt.setString(8, evaluationDTO.getEvaluationContent());

pstmt.setString(9, evaluationDTO.getTotalScore());

pstmt.setString(10, evaluationDTO.getCreditScore());

pstmt.setString(11, evaluationDTO.getComfortableScore());

pstmt.setString(12, evaluationDTO.getLectureScore());

return pstmt.executeUpdate();

} catch (Exception e) {

e.printStackTrace();

} finally {

try {

if(pstmt != null) pstmt.close();

if(conn != null) conn.close();

} catch (Exception e) {

e.printStackTrace();

}

}

return -1;

}

}


  ▶ reportAction.jsp: 사용자가 신고서를 작성하면, 관리자의 메일로 그 신고 내용을 받습니다.


<%@page import="javax.mail.Transport"%>

<%@page import="javax.mail.Message"%>

<%@page import="javax.mail.Address"%>

<%@page import="javax.mail.internet.InternetAddress"%>

<%@page import="javax.mail.internet.MimeMessage"%>

<%@page import="javax.mail.Session"%>

<%@page import="javax.mail.Authenticator"%>

<%@page import="java.util.Properties"%>

<%@page import="java.io.PrintWriter"%>

<%@page import="user.UserDAO"%>

<%@page import="util.SHA256"%>

<%@page import="util.Gmail"%>

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

<%

String userID = null;

if(session.getAttribute("userID") != null) {

userID = (String) session.getAttribute("userID");

}

if(userID == null) {

PrintWriter script = response.getWriter();

script.println("<script>");

script.println("alert('로그인을 해주세요.');");

script.println("location.href = 'userLogin.jsp'");

script.println("</script>");

script.close();

return;

}

request.setCharacterEncoding("UTF-8");

String reportTitle = null;

String reportContent = null;

if(request.getParameter("reportTitle") != null) {

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

}

if(request.getParameter("reportContent") != null) {

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

}

if (reportTitle == null || reportContent == null) {

PrintWriter script = response.getWriter();

script.println("<script>");

script.println("alert('입력이 안 된 사항이 있습니다.');");

script.println("history.back();");

script.println("</script>");

script.close();

return;

}

// 사용자에게 보낼 메시지를 기입합니다.

String host = "http://localhost:8080/Lecture_Evaluation/";

String from = "구글 계정 아이디";

String to = "관리자/개발자의 이메일 주소";

String subject = "강의평가 사이트에서 접수된 신고 메일입니다.";

String content = "신고자: " + userID + "<br>제목: " + reportTitle + "<br>내용: " + reportContent;

// SMTP에 접속하기 위한 정보를 기입합니다.

Properties p = new Properties();

p.put("mail.smtp.user", from);

p.put("mail.smtp.host", "smtp.googlemail.com");

p.put("mail.smtp.port", "465");

p.put("mail.smtp.starttls.enable", "true");

p.put("mail.smtp.auth", "true");

p.put("mail.smtp.debug", "true");

p.put("mail.smtp.socketFactory.port", "465");

p.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");

p.put("mail.smtp.socketFactory.fallback", "false");

 

try{

    Authenticator auth = new Gmail();

    Session ses = Session.getInstance(p, auth);

    ses.setDebug(true);

    MimeMessage msg = new MimeMessage(ses); 

    msg.setSubject(subject);

    Address fromAddr = new InternetAddress(from);

    msg.setFrom(fromAddr);

    Address toAddr = new InternetAddress(to);

    msg.addRecipient(Message.RecipientType.TO, toAddr);

    msg.setContent(content, "text/html;charset=UTF-8");

    Transport.send(msg);

} catch(Exception e){

    e.printStackTrace();

PrintWriter script = response.getWriter();

script.println("<script>");

script.println("alert('오류가 발생했습니다.');");

script.println("history.back();");

script.println("</script>");

script.close();

    return;

}

PrintWriter script = response.getWriter();

script.println("<script>");

script.println("alert('정상적으로 신고되었습니다.');");

script.println("history.back();");

script.println("</script>");

script.close();

    return;

%>


  ▶ evaluationRegisterAction.jsp: 사용자가 평가를 등록하게 되면, 그 평가 등록을 처리합니다.


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

<%@ page import="evaluation.EvaluationDTO"%>

<%@ page import="evaluation.EvaluationDAO"%>

<%@ page import="java.io.PrintWriter"%>

<%

request.setCharacterEncoding("UTF-8");


String userID = null;

if(session.getAttribute("userID") != null) {

userID = (String) session.getAttribute("userID");

}

if(userID == null) {

PrintWriter script = response.getWriter();

script.println("<script>");

script.println("alert('로그인을 해주세요.');");

script.println("location.href = 'userLogin.jsp'");

script.println("</script>");

script.close();

return;

}

request.setCharacterEncoding("UTF-8");

String lectureName = null;

String professorName = null;

int lectureYear = 0;

String semesterDivide = null;

String lectureDivide = null;

String evaluationTitle = null;

String evaluationContent = null;

String totalScore = null;

String creditScore = null;

String comfortableScore = null;

String lectureScore = null;

if(request.getParameter("lectureName") != null) {

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

}

if(request.getParameter("professorName") != null) {

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

}

if(request.getParameter("lectureYear") != null) {

try {

lectureYear = Integer.parseInt(request.getParameter("lectureYear"));

} catch (Exception e) {

System.out.println("강의 연도 데이터 오류");

}

}

if(request.getParameter("semesterDivide") != null) {

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

}

if(request.getParameter("lectureDivide") != null) {

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

}

if(request.getParameter("evaluationTitle") != null) {

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

}

if(request.getParameter("evaluationContent") != null) {

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

}

if(request.getParameter("totalScore") != null) {

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

}

if(request.getParameter("creditScore") != null) {

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

}

if(request.getParameter("comfortableScore") != null) {

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

}

if(request.getParameter("lectureScore") != null) {

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

}

if (lectureName == null || professorName == null || lectureYear == 0 || semesterDivide == null ||

lectureDivide == null || evaluationTitle == null || evaluationContent == null || totalScore == null ||

creditScore == null || comfortableScore == null || lectureScore == null ||

evaluationTitle.equals("") || evaluationContent.equals("")) {

PrintWriter script = response.getWriter();

script.println("<script>");

script.println("alert('입력이 안 된 사항이 있습니다.');");

script.println("history.back();");

script.println("</script>");

script.close();

return;

} else {

EvaluationDAO evaluationDAO = new EvaluationDAO();

int result = evaluationDAO.write(new EvaluationDTO(0, userID, lectureName, professorName, lectureYear,

semesterDivide, lectureDivide, evaluationTitle, evaluationContent,

totalScore, creditScore, comfortableScore, lectureScore, 0));

if (result == -1) {

PrintWriter script = response.getWriter();

script.println("<script>");

script.println("alert('평가 등록에 실패했습니다.');");

script.println("history.back();");

script.println("</script>");

script.close();

return;

} else {

PrintWriter script = response.getWriter();

script.println("<script>");

script.println("location.href = './index.jsp';");

script.println("</script>");

script.close();

return;

}

}

%>


  이제 한 번 테스트를 해봅시다.



  위와 같이 신고를 해보겠습니다.


  

  그럼 위와 같이 관리자 이메일로 신고 내용이 도착하는 것을 알 수 있습니다.


  평가 또한 등록해보도록 하겠습니다.



  성공적으로 평가 데이터가 데이터베이스에 등록되었습니다.

728x90
반응형

Comment +7

  • 김혁유 2018.05.14 23:56

    관리자의 승인을 기다리고 있는 댓글입니다

  • 살길바라냐 2018.06.14 13:07

    인프런에 답변이 없으셔서요 jdbc 최신버전인데도 안되네요 해결방법 없나요?


    Loading class com.mysql.jdbc.Driver'. This is deprecated. The new driver class iscom.mysql.cj.jdbc.Driver’. The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary.
    Thu Jun 14 00:00:33 KST 2018 WARN: Establishing SSL connection without server’s identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn’t set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to ‘false’. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
    java.sql.SQLException: The server time zone value ‘´???¹?±¹ ???ؽ?’ is unrecognized or represents more than one time zone. You must configure either the server or JDBC driver (via the serverTimezone configuration property) to use a more specifc time zone value if you want to utilize time zone support.
    at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:127)
    at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:95)
    at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:87)
    at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:61)
    at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:71)
    at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:76)
    at com.mysql.cj.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:862)
    at com.mysql.cj.jdbc.ConnectionImpl.(ConnectionImpl.java:444)
    at com.mysql.cj.jdbc.ConnectionImpl.getInstance(ConnectionImpl.java:230)
    at com.mysql.cj.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:226)
    at java.sql.DriverManager.getConnection(Unknown Source)
    at java.sql.DriverManager.getConnection(Unknown Source)
    at util.DatabaseUtil.getConnection(DatabaseUtil.java:14)
    at user.UserDAO.join(UserDAO.java:14)
    at org.apache.jsp.userJoinAction_jsp._jspService(userJoinAction_jsp.java:139)
    at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
    at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:443)
    at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:386)
    at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:330)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:496)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81)
    at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:650)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:803)
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:790)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1468)
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Unknown Source)
    Caused by: com.mysql.cj.exceptions.InvalidConnectionAttributeException: The server time zone value ‘´???¹?±¹ ???ؽ?’ is unrecognized or represents more than one time zone. You must configure either the server or JDBC driver (via the serverTimezone configuration property) to use a more specifc time zone value if you want to utilize time zone support.
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
    at java.lang.reflect.Constructor.newInstance(Unknown Source)
    at com.mysql.cj.exceptions.ExceptionFactory.createException(ExceptionFactory.java:59)
    at com.mysql.cj.exceptions.ExceptionFactory.createException(ExceptionFactory.java:83)
    at com.mysql.cj.util.TimeUtil.getCanonicalTimezone(TimeUtil.java:128)
    at com.mysql.cj.protocol.a.NativeProtocol.configureTimezone(NativeProtocol.java:2201)
    at com.mysql.cj.protocol.a.NativeProtocol.initServerSession(NativeProtocol.java:2225)
    at com.mysql.cj.jdbc.ConnectionImpl.initializePropsFromServer(ConnectionImpl.java:1391)
    at com.mysql.cj.jdbc.ConnectionImpl.connectOneTryOnly(ConnectionImpl.java:993)
    at com.mysql.cj.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:852)
    … 36 more
    java.lang.NullPointerException
    at user.UserDAO.join(UserDAO.java:15)
    at org.apache.jsp.userJoinAction_jsp._jspService(userJoinAction_jsp.java:139)
    at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
    at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:443)
    at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:386)
    at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:330)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:496)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81)
    at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:650)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:803)
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:790)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1468)
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Unknown Source)

  • 초보입니다. 2018.07.01 16:08

    관리자의 승인을 기다리고 있는 댓글입니다

  • 2019.06.19 23:32

    비밀댓글입니다

  • 2019.10.02 01:42

    비밀댓글입니다

728x90
반응형

  이번 시간은 JSP 강의평가(Lecture Evaluation) 웹 사이트 개발하기의 여섯 번째 시간입니다. 지난 시간에는 회원 데이터를 모델링하는 시간을 가져보았습니다. 이번 시간에는 모델링한 DTO, DAO를 이용하여 실제로 회원가입을 수행하고, 이메일을 전송하는 기능을 구현해보도록 하겠습니다. 기본적으로 이러한 기능을 위해서는 두 가지 유틸리티가 필요합니다. 하나는 회원가입 및 이메일 인증에 사용할 해시 데이터를 생성할 수 있는 SHA256 클래스이고, 다른 하나는 사용자에게 SMTP를 이용해 이메일 인증 메시지를 전송하는 이메일 전송 관련 Gmail 클래스입니다. 바로 다음과 같이 util 패키지를 생성해서 두 개의 클래스를 만들어주시면 됩니다.



  이후에 SMTP를 이용해 메일을 전송하기 위해서는 몇 가지 추가적인 외부 라이브러리가 필요합니다. 첫 번째는 Mail 라이브러리이며 다른 하나는 Activation 클래스입니다. 라이브러리가 없으신 분들은 다음의 링크에서 받아서 이용하시면 됩니다.


  ▶ Mail 라이브러리 다운로드 사이트: http://www.oracle.com/technetwork/java/javasebusiness/downloads/java-archive-downloads-eeplat-419426.html#javamail-1.4.7-oth-JPR


  ▶ Activation 라이브러리 다운로드 사이트: http://www.oracle.com/technetwork/java/javasebusiness/downloads/java-archive-downloads-java-plat-419418.html#jaf-1.1-fr-oth-JPR


  위 사이트로 접속하셔서 각각 javamail.zip과 jaf.zip를 다운로드 받으신 뒤에 압축을 푸셔서 라이브러리를 찾아서 다음과 같이 WEB-INF 폴더 내부의 lib 폴더에 넣어주시면 됩니다.



  이제 한 번 우리의 util 패키지 내에 있는 클래스들을 작성해보도록 하겠습니다.


  ▶ SHA256.java


package util;


import java.security.MessageDigest;


public class SHA256 {


public static String getSHA256(String input) {

StringBuffer result = new StringBuffer();

try {

MessageDigest digest = MessageDigest.getInstance("SHA-256");

byte[] salt = "Hello! This is Salt.".getBytes();

digest.reset();

digest.update(salt);

byte[] chars = digest.digest(input.getBytes("UTF-8"));

for(int i = 0; i < chars.length; i++) {

String hex = Integer.toHexString(0xff & chars[i]);

if(hex.length() == 1) result.append('0');

result.append(hex);

}

} catch (Exception e) {

e.printStackTrace();

}

return result.toString();

}

}


  위 소스코드를 보시면 솔트(Salt) 부분이 들어가있는 것을 알 수 있습니다. 해시 데이터를 생성할 때 악의적인 목적을 가진 공격자가 원래의 데이터를 파악하기 더욱 어렵게 만들어 줄 수 있습니다.


  ▶ Gmail.java


package util;


import javax.mail.Authenticator;

import javax.mail.PasswordAuthentication;


public class Gmail extends Authenticator {


    @Override

    protected PasswordAuthentication getPasswordAuthentication() {

        return new PasswordAuthentication("아이디","비밀번호");

    }

    

}


  SMTP에서 인증을 위한 부분을 위와 같이 클래스에서 작성할 수 있습니다. 저는 구글 이메일(G-Mail)을 사용해보도록 하겠습니다. 자신의 구글 계정 중에서 이메일을 사용할 수 있는 계정을 입력하시면 됩니다. 또한 기본적으로 구글 계정은 '계정' 설정에서 '보안 수준이 낮은 앱'에 대한 접근 권한이 설정되어야 합니다.



  이제 다음과 같이 userRegisterAction.jsp, emailCheckAction.jsp, emailSendAction.jsp 세 가지를 만들어줍니다.



▶ userRegisterAction.jsp: 회원가입을 처리합니다. 사용자 정보를 데이터베이스에 등록한 이후에, 자동으로 사용자의 이메일로 이메일을 전송하고 인증을 기다립니다.


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

<%@ page import="user.UserDTO"%>

<%@ page import="user.UserDAO"%>

<%@ page import="util.SHA256"%>

<%@ page import="java.io.PrintWriter"%>

<%

request.setCharacterEncoding("UTF-8");

String userID = null;

String userPassword = null;

String userEmail = null;

if(request.getParameter("userID") != null) {

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

}

if(request.getParameter("userPassword") != null) {

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

}

if(request.getParameter("userEmail") != null) {

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

}

if (userID == null || userPassword == null || userEmail == null) {

PrintWriter script = response.getWriter();

script.println("<script>");

script.println("alert('입력이 안 된 사항이 있습니다.');");

script.println("history.back();");

script.println("</script>");

script.close();

} else {

UserDAO userDAO = new UserDAO();

int result = userDAO.join(new UserDTO(userID, userPassword, userEmail, SHA256.getSHA256(userEmail), false));

if (result == -1) {

PrintWriter script = response.getWriter();

script.println("<script>");

script.println("alert('이미 존재하는 아이디입니다.');");

script.println("history.back();");

script.println("</script>");

script.close();

} else {

session.setAttribute("userID", userID);

PrintWriter script = response.getWriter();

script.println("<script>");

script.println("location.href = 'emailSendAction.jsp';");

script.println("</script>");

script.close();

}

}

%>


▶ emailSendAction.jsp: 이메일 인증 메시지를 이메일을 통해 전송합니다.


<%@page import="javax.mail.Transport"%>

<%@page import="javax.mail.Message"%>

<%@page import="javax.mail.Address"%>

<%@page import="javax.mail.internet.InternetAddress"%>

<%@page import="javax.mail.internet.MimeMessage"%>

<%@page import="javax.mail.Session"%>

<%@page import="javax.mail.Authenticator"%>

<%@page import="java.util.Properties"%>

<%@page import="java.io.PrintWriter"%>

<%@page import="user.UserDAO"%>

<%@page import="util.SHA256"%>

<%@page import="util.Gmail"%>

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

<%

UserDAO userDAO = new UserDAO();

String userID = null;

if(session.getAttribute("userID") != null) {

userID = (String) session.getAttribute("userID");

}

if(userID == null) {

PrintWriter script = response.getWriter();

script.println("<script>");

script.println("alert('로그인을 해주세요.');");

script.println("location.href = 'userLogin.jsp'");

script.println("</script>");

script.close();

return;

}

boolean emailChecked = userDAO.getUserEmailChecked(userID);

if(emailChecked == true) {

PrintWriter script = response.getWriter();

script.println("<script>");

script.println("alert('이미 인증 된 회원입니다.');");

script.println("location.href = 'index.jsp'");

script.println("</script>");

script.close();

return;

}

// 사용자에게 보낼 메시지를 기입합니다.

String host = "http://localhost:8080/Lecture_Evaluation/";

String from = "이메일 아이디";

String to = userDAO.getUserEmail(userID);

String subject = "강의평가를 위한 이메일 확인 메일입니다.";

String content = "다음 링크에 접속하여 이메일 확인을 진행하세요." +

"<a href='" + host + "emailCheckAction.jsp?code=" + new SHA256().getSHA256(to) + "'>이메일 인증하기</a>";

// SMTP에 접속하기 위한 정보를 기입합니다.

Properties p = new Properties();

p.put("mail.smtp.user", from);

p.put("mail.smtp.host", "smtp.googlemail.com");

p.put("mail.smtp.port", "465");

p.put("mail.smtp.starttls.enable", "true");

p.put("mail.smtp.auth", "true");

p.put("mail.smtp.debug", "true");

p.put("mail.smtp.socketFactory.port", "465");

p.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");

p.put("mail.smtp.socketFactory.fallback", "false");

 

try{

    Authenticator auth = new Gmail();

    Session ses = Session.getInstance(p, auth);

    ses.setDebug(true);

    MimeMessage msg = new MimeMessage(ses); 

    msg.setSubject(subject);

    Address fromAddr = new InternetAddress(from);

    msg.setFrom(fromAddr);

    Address toAddr = new InternetAddress(to);

    msg.addRecipient(Message.RecipientType.TO, toAddr);

    msg.setContent(content, "text/html;charset=UTF-8");

    Transport.send(msg);

} catch(Exception e){

    e.printStackTrace();

PrintWriter script = response.getWriter();

script.println("<script>");

script.println("alert('오류가 발생했습니다..');");

script.println("history.back();");

script.println("</script>");

script.close();

    return;

}

%>

<!doctype html>

<html>

  <head>

    <title>강의평가 웹 사이트</title>

    <meta charset="utf-8">

    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- 부트스트랩 CSS 추가하기 -->

    <link rel="stylesheet" href="./css/bootstrap.min.css">

    <!-- 커스텀 CSS 추가하기 -->

    <link rel="stylesheet" href="./css/custom.css">

  </head>

  <body>

    <nav class="navbar navbar-expand-lg navbar-light bg-light">

      <a class="navbar-brand" href="index.jsp">강의평가 웹 사이트</a>

      <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar">

        <span class="navbar-toggler-icon"></span>

      </button>

      <div class="collapse navbar-collapse" id="navbar">

        <ul class="navbar-nav mr-auto">

          <li class="nav-item active">

            <a class="nav-link" href="index.jsp">메인</a>

          </li>

          <li class="nav-item dropdown">

            <a class="nav-link dropdown-toggle" id="dropdown" data-toggle="dropdown">

              회원 관리

            </a>

            <div class="dropdown-menu" aria-labelledby="dropdown">

              <a class="dropdown-item" href="userLogin.jsp">로그인</a>

            </div>

          </li>

        </ul>

        <form action="./index.jsp" method="get" class="form-inline my-2 my-lg-0">

          <input type="text" name="search" class="form-control mr-sm-2" placeholder="내용을 입력하세요.">

          <button class="btn btn-outline-success my-2 my-sm-0" type="submit">검색</button>

        </form>

      </div>

    </nav>

<div class="container">

    <div class="alert alert-success mt-4" role="alert">

  이메일 주소 인증 메일이 전송되었습니다. 이메일에 들어가셔서 인증해주세요.

</div>

    </div>

    <footer class="bg-dark mt-4 p-5 text-center" style="color: #FFFFFF;">

      Copyright ⓒ 2018 나동빈 All Rights Reserved.

    </footer>

    <!-- 제이쿼리 자바스크립트 추가하기 -->

    <script src="./js/jquery.min.js"></script>

    <!-- Popper 자바스크립트 추가하기 -->

    <script src="./js/popper.min.js"></script>

    <!-- 부트스트랩 자바스크립트 추가하기 -->

    <script src="./js/bootstrap.min.js"></script>

  </body>

</html>


▶ emailCheckAction.jsp: 사용자가 이메일 인증을 하게 되면 그에 대한 정보를 처리하는 페이지입니다.


<%@page import="java.io.PrintWriter"%>

<%@page import="util.SHA256"%>

<%@page import="user.UserDAO"%>

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

<%

request.setCharacterEncoding("UTF-8");

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

UserDAO userDAO = new UserDAO();

String userID = null;

if(session.getAttribute("userID") != null) {

userID = (String) session.getAttribute("userID");

}

if(userID == null) {

PrintWriter script = response.getWriter();

script.println("<script>");

script.println("alert('로그인을 해주세요.');");

script.println("location.href = 'userLogin.jsp'");

script.println("</script>");

script.close();

return;

}

String userEmail = userDAO.getUserEmail(userID);

boolean rightCode = (new SHA256().getSHA256(userEmail).equals(code)) ? true : false;

if(rightCode == true) {

userDAO.setUserEmailChecked(userID);

PrintWriter script = response.getWriter();

script.println("<script>");

script.println("alert('인증에 성공했습니다.');");

script.println("location.href = 'index.jsp'");

script.println("</script>");

script.close();

return;

} else {

PrintWriter script = response.getWriter();

script.println("<script>");

script.println("alert('유효하지 않은 코드입니다.');");

script.println("location.href = 'index.jsp'");

script.println("</script>");

script.close();

return;

}

%>


  이제 한 번 테스트를 해봅시다.



  먼저 위와 같이 회원가입을 해주겠습니다.



이후에 위와 같이 회원가입 된 정보를 확인합시다.



이후에 이메일 주소 인증 메일이 전송되었다는 메시지가 나오네요.



  이제 이메일 함으로 이동해보시면 위와 같이 인증 메일이 온 것을 확인할 수 있습니다.



  링크를 타고 이동하면 위와 같이 인증이 완료되어 메인 페이지가 등장합니다.



이후에 위와 같이 데이터베이스에서 사용자 정보를 확인하면 'userEmailChecked' 속성의 값이 1(True)로 바뀌어있는 것을 확인할 수 있습니다.

728x90
반응형

Comment +19

  • 살길바라냐 2018.06.21 08:57

    인프런에서 배운 userRegisterAction.jsp에서 codeunreachable 뜨네요 인프런과 여기 홈피에서 코딩이 많이 다른데 어디껄 따르면 되나요??

  • 2018.06.26 15:31

    비밀댓글입니다

  • 초보입니다. 2018.07.01 14:33

    org.apache.jasper.JasperException: Unable to compile class for JSP:

    An error occurred at line: [14] in the generated java file: [D:\Users\WH_Laptop\eclipse-workspace\.metadata\.plugins\org.eclipse.wst.server.core\tmp1\work\Catalina\localhost\Lecture_Evaluation\org\apache\jsp\userRegisterAction_jsp.java]
    Only a type can be imported. user.UserDTO resolves to a package

    An error occurred at line: [15] in the generated java file: [D:\Users\WH_Laptop\eclipse-workspace\.metadata\.plugins\org.eclipse.wst.server.core\tmp1\work\Catalina\localhost\Lecture_Evaluation\org\apache\jsp\userRegisterAction_jsp.java]
    Only a type can be imported. user.UserDAO resolves to a package

    An error occurred at line: [16] in the generated java file: [D:\Users\WH_Laptop\eclipse-workspace\.metadata\.plugins\org.eclipse.wst.server.core\tmp1\work\Catalina\localhost\Lecture_Evaluation\org\apache\jsp\userRegisterAction_jsp.java]
    Only a type can be imported. util.SHA256 resolves to a package

    An error occurred at line: [53] in the jsp file: [/userRegisterAction.jsp]
    UserDAO cannot be resolved to a type
    50:
    51: } else {
    52:
    53: UserDAO userDAO = new UserDAO();
    54:
    55: int result = userDAO.join(new UserDTO(userID, userPassword, userEmail, SHA256.getSHA256(userEmail), false));
    56:


    An error occurred at line: [53] in the jsp file: [/userRegisterAction.jsp]
    UserDAO cannot be resolved to a type
    50:
    51: } else {
    52:
    53: UserDAO userDAO = new UserDAO();
    54:
    55: int result = userDAO.join(new UserDTO(userID, userPassword, userEmail, SHA256.getSHA256(userEmail), false));
    56:


    An error occurred at line: [55] in the jsp file: [/userRegisterAction.jsp]
    UserDTO cannot be resolved to a type
    52:
    53: UserDAO userDAO = new UserDAO();
    54:
    55: int result = userDAO.join(new UserDTO(userID, userPassword, userEmail, SHA256.getSHA256(userEmail), false));
    56:
    57: if (result == -1) {
    58:


    An error occurred at line: [55] in the jsp file: [/userRegisterAction.jsp]
    SHA256 cannot be resolved
    52:
    53: UserDAO userDAO = new UserDAO();
    54:
    55: int result = userDAO.join(new UserDTO(userID, userPassword, userEmail, SHA256.getSHA256(userEmail), false));
    56:
    57: if (result == -1) {
    58:


    Stacktrace:
    org.apache.jasper.compiler.DefaultErrorHandler.javacError(DefaultErrorHandler.java:102)
    org.apache.jasper.compiler.ErrorDispatcher.javacError(ErrorDispatcher.java:212)
    org.apache.jasper.compiler.JDTCompiler.generateClass(JDTCompiler.java:495)
    org.apache.jasper.compiler.Compiler.compile(Compiler.java:379)
    org.apache.jasper.compiler.Compiler.compile(Compiler.java:351)
    org.apache.jasper.compiler.Compiler.compile(Compiler.java:335)
    org.apache.jasper.JspCompilationContext.compile(JspCompilationContext.java:595)
    org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:368)
    org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:386)
    org.apache.jasper.servlet.JspServlet.service(JspServlet.java:330)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
    org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)


    인프런 강의를 보면서 실습 중인데여 404 에러가 떠야하는데 500에러가 뜨면서 이렇게 나옵니다.
    분명 다 맞게 썻는데 이러네요 혹시나 하고 복붙을 해봐도 안됩니다.

    아 그리고 mysql 설치할때 3306 포트가 안되서 3305로 했는데 여기서 영향이 생긴건가요?

  • 도와주세요.. 2018.11.12 15:37

    3일째 해결이 안되네요.. 부탁드립니다.
    에러 내용은 아래 에 첨부 하겠습니다.

    EBUG: setDebug: JavaMail version 1.4.7
    DEBUG: getProvider() returning javax.mail.Provider[TRANSPORT,smtp,com.sun.mail.smtp.SMTPTransport,Oracle]
    DEBUG SMTP: useEhlo true, useAuth true
    DEBUG SMTP: useEhlo true, useAuth true
    DEBUG SMTP: trying to connect to host "smtp.googlemail.com", port 465, isSSL false
    Could not connect to SMTP host: smtp.googlemail.com, port: 465
    javax.mail.MessagingException: Could not connect to SMTP host: smtp.googlemail.com, port: 465;
    nested exception is: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

  • 이상열 2018.12.09 02:30

    int result = userDAO.join(new userDTO(userID,userPassword,userEmail,SHA256.getSHA256(userEmail),false));
    이부분이 계속 오류가 나는거로 표시가 됩니다.

  • mrblack 2019.03.03 21:47

    안녕하세요?
    위 강의 따라 하던중 회원 가입을 하면 "이미 존재하는 아이디" 라고 나옵니다.
    데이터베이스에는 아무 내용도 없습니다.
    심지어 아무것도 입력하지 않고 회원가입 눌러도 "이미 존재하는 아이디" 라고 나옵니다.
    userRegisterAction.jsp에서 email 처리부분을 주석 처리하고 아무것도 입력하지 않고 회원가입 버튼 누르면 그제서야 "입력이 안된 사항이 있습니다"라고 나옵니다.

    if(request.getParameter("userEmail") != null) {
    userEmail = (String) request.getParameter("userEmail");
    }

    이부분이 계속 처리가 안되는것 같은데요.. 원인을 알 수 있을까요?

  • 2019.05.07 23:16

    비밀댓글입니다

  • 2019.06.04 07:35

    비밀댓글입니다

  • indiana 2019.07.14 16:33

    안녕하세요! 강의 잘보면서 따라하고 있는데,
    궁금한게 있어서요..
    제가 메일을 보내면 관리자 메일로도 회원가입한 사람 메일에 간 인증메일이 똑같이 보내지는데
    원래 그런건가요 아니면 제가 어디서 실수한거일까요..?

  • 2019.08.05 14:58

    비밀댓글입니다

  • 1234 2019.08.05 14:59

    버2ㅣㄷ

  • 지지 2019.09.21 17:04

    int result = userDAO.join(new UserDTO(userID, userPassword, userEmail, SHA256.getSHA256(userEmail),false));

    이 구문에서 500에러 발생하는데 어떻게 해결하나요 ?

  • 안녕하세요 2019.10.17 23:26

    535-5.7.8 Username and Password not accepted. Learn more at
    535 5.7.8 https://support.google.com/mail/?p=BadCredentials v2sm2787590pgf.39 - gsmtp
    오류가 조회되는데요 보안설정에서 낮은 수준액세스 허용도 해보았고 계정의 아이디 비번 다 체크해도
    이상이없는데 위와같이 오류가 조회됩니다. 원인이 무엇인지 찾지를 못하고 있습니다.

  • 개발꿈나무 2019.11.13 22:07

    도메인과 서버를 샀는데요 도메인에 올렸을때는 왜 이메일 전송이 안될까요? localhost에서는 이메일 전송이 됩니다.

  • 2019.12.09 18:17

    org.apache.jasper.JasperException: 행 [57]에서 [userRegisterAction.jsp]을(를) 처리하는 중 예외 발생


    55: UserDAO userDAO = new UserDAO();
    56:
    57: int result = userDAO.join(new UserDTO(userID, userPassword, userEmail, SHA256.getSHA256(userEmail), false));
    58:
    59: if (result == -1) {

    오류가나오는대 무엇떄문일까요

  • 문상우 2020.02.21 15:30

    jsp 공부중인 학생입니다.

    DEBUG: setDebug: JavaMail version 1.4.7
    DEBUG: getProvider() returning javax.mail.Provider[TRANSPORT,smtp,com.sun.mail.smtp.SMTPTransport,Oracle]
    DEBUG SMTP: useEhlo true, useAuth true
    DEBUG SMTP: useEhlo true, useAuth true
    DEBUG SMTP: trying to connect to host "smtp.gmail.co.kr", port 465, isSSL true
    javax.mail.MessagingException: Unknown SMTP host: smtp.gmail.co.kr;
    nested exception is:

    포트 번호도 바꾸어 보고 했지만 게속 해서 오류가 없어 지지 않아서 ...
    마땅히 오류해결할 방법을 찾지 못해서요 ..

  • 아니진자 2020.10.09 19:37

    관리자의 승인을 기다리고 있는 댓글입니다

728x90
반응형

  이번 강의는 JSP 강의평가(Lecture Evaluation) 웹 사이트 개발하기의 다섯 번째 시간입니다. 이번 시간에는 지난 시간에 했었던 데이터베이스 설계를 토대로 서버 프로그램상에서 데이터를 모델링하려고 합니다. 일반적으로 데이터를 모델링할 때는 DTO(Data Transfer Object)와 DAO(Data Access Object)를 작성해주시면 됩니다. DTO는 말 그대로 데이터베이스 테이블과 거의 1:1 대응되어 개별 데이터 원소를 보관하기 위해 사용되는 객체이며 DAO는 데이터베이스와 실질적으로 통신하는 모듈입니다. 이번 시간에는 가장 먼저 회원 데이터를 모델링해보도록 하겠습니다.



  먼저 위와 같이 자바 소스코드 영역에 user 패키지를 생성해서 UserDAO와 UserDTO를 자바 파일로 생성합니다.


  ▶ UserDTO.java


package user;


public class UserDTO {


private String userID;

private String userPassword;

private String userEmail;

private String userEmailHash;

private boolean userEmailChecked;

public String getUserID() {

return userID;

}

public void setUserID(String userID) {

this.userID = userID;

}

public String getUserPassword() {

return userPassword;

}

public void setUserPassword(String userPassword) {

this.userPassword = userPassword;

}

public String getUserEmail() {

return userEmail;

}

public void setUserEmail(String userEmail) {

this.userEmail = userEmail;

}

public String getUserEmailHash() {

return userEmailHash;

}

public void setUserEmailHash(String userEmailHash) {

this.userEmailHash = userEmailHash;

}

public boolean isUserEmailChecked() {

return userEmailChecked;

}

public void setUserEmailChecked(boolean userEmailChecked) {

this.userEmailChecked = userEmailChecked;

}

public UserDTO(String userID, String userPassword, String userEmail,

String userEmailHash, boolean userEmailChecked) {

this.userID = userID;

this.userPassword = userPassword;

this.userEmail = userEmail;

this.userEmailHash = userEmailHash;

this.userEmailChecked = userEmailChecked;

}


}


  위 DTO 파일을 보시면 사실상 이전 시간에 작업했던 데이터베이스 부분과 거의 일치하는 것을 알 수 있습니다. MySQL 언어를 자바 소스코드 언어로 바꾼 것 뿐이지요.


  ▶ UserDAO.java


package user;


import java.sql.Connection;

import java.sql.DriverManager;

import java.sql.PreparedStatement;

import java.sql.ResultSet;

import java.sql.SQLException;


public class UserDAO {


private Connection conn;

private ResultSet rs;


public UserDAO() {

try {

String dbURL = "jdbc:mysql://localhost:3306/LectureEvaluation";

String dbID = "root";

String dbPassword = "root1234";

Class.forName("com.mysql.jdbc.Driver");

conn = DriverManager.getConnection(dbURL, dbID, dbPassword);

} catch (Exception e) {

e.printStackTrace();

}

}

public int login(String userID, String userPassword) {

String SQL = "SELECT userPassword FROM USER WHERE userID = ?";

try {

PreparedStatement pstmt = conn.prepareStatement(SQL);

pstmt.setString(1, userID);

rs = pstmt.executeQuery();

if(rs.next()) {

if(rs.getString(1).equals(userPassword))

return 1; // 로그인 성공

else

return 0; // 비밀번호 틀림

}

return -1; // 아이디 없음

} catch (SQLException e) {

e.printStackTrace();

}

return -2; // 데이터베이스 오류

}

public int join(UserDTO user) {

String SQL = "INSERT INTO USER VALUES (?, ?, ?, ?, false)";

try {

PreparedStatement pstmt = conn.prepareStatement(SQL);

pstmt.setString(1, user.getUserID());

pstmt.setString(2, user.getUserPassword());

pstmt.setString(3, user.getUserEmail());

pstmt.setString(4, user.getUserEmailHash());

return pstmt.executeUpdate();

} catch (SQLException e) {

e.printStackTrace();

}

return -1; // 회원가입 실패

}

public String getUserEmail(String userID) {

String SQL = "SELECT userEmail FROM USER WHERE userID = ?";

try {

PreparedStatement pstmt = conn.prepareStatement(SQL);

pstmt.setString(1, userID);

rs = pstmt.executeQuery();

while(rs.next()) {

return rs.getString(1); // 이메일 주소 반환

}

} catch (SQLException e) {

e.printStackTrace();

}

return null; // 데이터베이스 오류

}

public boolean getUserEmailChecked(String userID) {

String SQL = "SELECT userEmailChecked FROM USER WHERE userID = ?";

try {

PreparedStatement pstmt = conn.prepareStatement(SQL);

pstmt.setString(1, userID);

rs = pstmt.executeQuery();

while(rs.next()) {

return rs.getBoolean(1); // 이메일 등록 여부 반환

}

} catch (SQLException e) {

e.printStackTrace();

}

return false; // 데이터베이스 오류

}

public boolean setUserEmailChecked(String userID) {

String SQL = "UPDATE USER SET userEmailChecked = true WHERE userID = ?";

try {

PreparedStatement pstmt = conn.prepareStatement(SQL);

pstmt.setString(1, userID);

pstmt.executeUpdate();

return true; // 이메일 등록 설정 성공

} catch (SQLException e) {

e.printStackTrace();

}

return false; // 이메일 등록 설정 실패

}

}


  UserDAO에서는 실제로 데이터베이스와 통신하는 부분이 들어있습니다. 보시면 생성자는 데이터베이스와 연동하는 부분임을 알 수 있습니다. 나머지 함수들은 각각 데이터베이스와 통신하는 유의미한 함수들입니다.

  - login(): 아이디와 비밀번호를 받아서, 로그인을 시도하는 함수입니다. 결과는 정수형으로 반환됩니다.
  - join(): 사용자의 정보를 입력 받아서 회원가입을 수행하는 함수입니다. 결과는 정수형으로 반환됩니다.

  - getUserEmail(): 사용자의 아이디를 이용해 이메일 주소를 알아냅니다. 결과는 문자열형으로 반환됩니다.

  - getUserEmailChecked(): 사용자가 현재 이메일 인증이 되었는지 확인하는 함수입니다. 결과는 참/값형으로 반환됩니다.
  - setUserEmailChecked(): 특정한 사용자의 이메일 인증을 수행해줍니다.

  또한 사실 이 DAO는 잘못 설계된 소스코드에요. 원래는 conn 객체를 매 함수마다 새롭게 초기화주어 사용하셔야 합니다. 지금 제 DAO 같은 경우는 접속자가 많은 경우 데이터베이스 연결 갯수 초과 오류가 뜰 수 있어요. 데이터베이스 커넥션 풀을 사용하는 방식으로 효과적으로 해결할 수도 있구요. 하지만 저는 이러한 문제를 알고도 그냥 진행하겠습니다. 왜냐면 귀찮거든요. 작은 프로젝트니까 그냥 넘어갈게요. ㅋㅋㅋ


  이제 데이터베이스 연동을 위해서 JDBC Connector를 설치해주도록 할게요. MySQL 전용 드라이버는 다음의 경로에서 구하실 수 있습니다.


  ▶ MySQL JDBC 드라이버 사이트: https://dev.mysql.com/downloads/connector/j/5.1.html



  위와 같이 프로젝트가 구성되도록 JDBC를 넣어주시면 된답니다. WEB-INF 폴더의 lib 폴더에 넣어주시면 자동으로 웹 어플리케이션이 구동되는 단계에서 해당 라이브러리를 사용할 수 있게 되서 문제가 없습니다.

728x90
반응형

Comment +0

728x90
반응형

  JSP로 강의평가(Lecture Evaluation) 웹 사이트 만들기 네 번째 시간입니다. 이번 시간에는 데이터베이스를 구축하는 시간을 가져보도록 할게요. 지난 시간에는 웹 사이트의 디자인을 전반적으로 처리를 하는 시간을 가졌으므로, 데이터베이스를 구축하는 것은 그다지 어렵지 않을 것 같습니다. 사실 웹 디자인을 작업하면서 설계적인 측면을 고려를 하셨을 것이기 때문이에요. 데이터베이스에서는 테이블(Table)이라는 것을 단위로 설정합니다. 하나의 데이터 모델을 테이블로 설정하는 거죠. 대표적으로 테이블의 예시로는 회원 정보, 강의 정보, 평가 정보 이런 식으로 들어갈 수 있습니다. 저희가 개발한 웹 디자인에서는 오직 세 개의 테이블만 만들어주면 됩니다. 바로 평가 테이블, 회원 테이블, 추천 테이블이랍니다. 일단 테이블을 만들기 이전에 데이터베이스를 생성하는 것부터 해보도록 합시다.


  MySQL에 접속한 이후에 다음의 두 명령어를 입력합니다.


  CREATE DATABASE LectureEvaluation; #데이터베이스 생성

  USE LectureEvaluation; #생성한 데이터베이스에 접속



  ▶ 평가 테이블(Evaluation Table)



CREATE TABLE EVALUATION (

  evaluationID int PRIMARY KEY AUTO_INCREMENT, #평가 번호

  userID varchar(50), #작성자 아이디

  lectureName varchar(50), #강의명

  professorName varchar(50), #교수명

  lectureYear int, #수강 연도

  semesterDivide varchar(20), #수강 학기

  lectureDivide varchar(10), #강의 구분

  evaluationTitle varchar(50), #평가 제목

  evaluationContent varchar(2048), #평가 내용

  totalScore varchar(10), #종합 점수

  creditScore varchar(10), #성적 점수

  comfortableScore varchar(10), #널널 점수

  lectureScore varchar(10), #강의 점수

  likeCount int #추천갯수

);



  위와 같이 데이터베이스 테이블을 설계할 수 있습니다. 기본적으로 어떠한 평가는 한 명의 사용자가 작성을 한다는 점에서 '작성자 아이디' 속성이 들어가는 것을 확인할 수 있습니다. 나머지는 단순하게 정수형은 int, 문자열형은 varchar를 사용해 자료형을 정했습니다. 기본적으로 위와 같이 웹 디자인을 보면서 작업하시는 것이 좋습니다.


  ▶ 회원 테이블(User Table)



CREATE TABLE USER (

  userID varchar(50), #작성자 아이디

  userPassword varchar(50), #작성자 비밀번호

  userEmail varchar(50), #작성자 이메일

  userEmailHash varchar(64), #이메일 확인 해시값

  userEmailChecked boolean #이메일 확인 여부

);



  위와 같이 데이터베이스 테이블을 작성할 수 있습니다. 특이한 부분이라면, 강의 평가를 무차별하게 남발할 수 없도록 하는 시빌 공격(Sybil Attack)의 기본적인 방어법이라고 할 수 있습니다. 이메일을 인증해야만 강의 평가를 달 수 있도록 하는 것입니다. 이 때는 해시 데이터가 사용되는 것이 합당할 것입니다. 이렇게 데이터베이스 구축이 완료되었습니다. 정말 쉽습니다.


  ▶ 추천 테이블(Like Table)


CREATE TABLE LIKEY (

  userID varchar(50), #작성자 아이디

  evaluationID int, #평가 번호

  userIP varchar(50) #작성자 아이피

);



  추천 테이블은 이름을 LIKEY로 설정했습니다. 사실상 관계 테이블(Relation Table)이라고 할 수 있답니다. LIKE는 MySQL 안에서 예약어로 사용이 되고 있기 때문에 LIKEY로 설정을 한 거에요.

728x90
반응형

Comment +2

728x90
반응형

  이번 시간은 JSP로 강의평가(Lecture Evaluation) 웹 사이트 개발하기 강좌의 두 번째 시간입니다. 지난 시간에는 강의평가 웹 사이트를 개발하기 위한 준비물에 대해서 알아보았습니다. 이번 시간은 웹 디자인을 해보도록 하겠습니다. 일반적으로 혼자서 웹 사이트를 개발할 때는 웹 서버 프로그램을 개발하기 전에 웹 디자인을 개발하는 것이 일반적입니다. 애초에 웹 디자인을 하기 위해서는 기본적인 설계가 잡혀있어야 되기 때문이지요. 바로 웹 디자인을 해보도록 하겠습니다.


  웹 디자인을 완전히 제로베이스에서 하나하나씩 하면 힘이 들기 마련입니다. 그러므로 프레임워크와 라이브러리를 사용합시다.


  1) 부트스트랩 프레임워크: http://getbootstrap.com/docs/4.0/getting-started/download/


  위 사이트에 가서 bootstrap.min.css와 bootstrap.min.js를 다운로드하도록 합시다. 부트스트랩은 웹 디자인의 메인 프레임워크로 사용되는 경우가 많습니다. 다만 메인 프레임워크를 가동시키기 위해서는 여러 개의 추가적인 라이브러리가 필요할 수 있지요.


  2) 파퍼(Popper) 라이브러리: https://unpkg.com/popper.js@1.12.9/dist/umd/popper.min.js


  위 사이트로 가서 부트스트랩 프레임워크의 내부 로직에 사용되는 파퍼 자바스크립트 라이브러리를 다운로드 받아주세요.


  3) 제이쿼리(jQuery) 라이브러리: https://jquery.com/download/


  위 사이트에 가신 뒤에 jQuery 압축 버전을 다운로드 받아주세요. 부트스트랩은 jQuery 라이브러리의 다양한 함수를 사용하는 프레임워크이므로 jQuery 라이브러리가 포함되어야 정상적으로 사용할 수 있습니다.


  이제 다음과 같이 이클립스 JEE를 실행하여 새로운 동적 웹 프로젝트를 생성해줍니다.



  이후에 다음과 같이 Lecture Evaluation이라는 이름으로 하나의 프로젝트를 생성해주겠습니다.



  그리고 다음과 같이 Lecture Evaluation 프로젝트 안에 css, js 폴더를 생성해서 그 안에 부트스트랩 프레임워크, jQuery 라이브러리, Popper 라이브러리를 모두 삽입해줍니다. 이후에 custom.css라는 이름으로 하나의 디자인 파일을 더 넣어줍니다. 여기에다가는 우리의 고유 디자인을 넣어 줄 겁니다. 바로 다음과 같이 프로젝트가 구성되도록 하면 됩니다.



  custom.css 파일의 소스코드는 다음과 같이 작성해주겠습니다.


@import url(https://fonts.googleapis.com/earlyaccess/jejugothic.css);

@import url(https://fonts.googleapis.com/earlyaccess/nanumgothic.css);

.navbar-brand, h1, h2, h3, h4 {

  font-family: 'Jeju Gothic';

}

h5 {

  font-family: 'Jeju Gothic';

  font-size: 18px;

}

body {

  font-family: 'Nanum Gothic';

}


  기본적으로 제주고딕과 나눔고딕을 웹 폰트로 적용해 준 것이 전부입니다. 이제 한 번 index.jsp 페이지를 생성해서 작성해봅시다.


<!doctype html>

<html>

  <head>

    <title>강의평가 웹 사이트</title>

    <meta charset="utf-8">

    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- 부트스트랩 CSS 추가하기 -->

    <link rel="stylesheet" href="./css/bootstrap.min.css">

    <!-- 커스텀 CSS 추가하기 -->

    <link rel="stylesheet" href="./css/custom.css">

  </head>

  <body>

    <nav class="navbar navbar-expand-lg navbar-light bg-light">

      <a class="navbar-brand" href="index.jsp">강의평가 웹 사이트</a>

      <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar">

        <span class="navbar-toggler-icon"></span>

      </button>

      <div class="collapse navbar-collapse" id="navbar">

        <ul class="navbar-nav mr-auto">

          <li class="nav-item active">

            <a class="nav-link" href="index.jsp">메인</a>

          </li>

          <li class="nav-item dropdown">

            <a class="nav-link dropdown-toggle" id="dropdown" data-toggle="dropdown">

              회원 관리

            </a>

            <div class="dropdown-menu" aria-labelledby="dropdown">

              <a class="dropdown-item" href="#">로그인</a>

              <a class="dropdown-item" href="#">회원가입</a>

              <a class="dropdown-item" href="#">로그아웃</a>

            </div>

          </li>

        </ul>

        <form class="form-inline my-2 my-lg-0">

          <input class="form-control mr-sm-2" type="search" placeholder="내용을 입력하세요." aria-label="Search">

          <button class="btn btn-outline-success my-2 my-sm-0" type="submit">검색</button>

        </form>

      </div>

    </nav>

    <!-- 제이쿼리 자바스크립트 추가하기 -->

    <script src="./js/jquery.min.js"></script>

    <!-- Popper 자바스크립트 추가하기 -->

    <script src="./js/popper.min.js"></script>

    <!-- 부트스트랩 자바스크립트 추가하기 -->

    <script src="./js/bootstrap.min.js"></script>

  </body>

</html>


  실행 결과는 다음과 같습니다.



728x90
반응형

Comment +5