반응형
SMALL

내 블로그 페이지 만들기

이젠 반복작업이다. 저번 포스팅의 위젯을 그대로 따고, 내 블로그 페이지에는 세션에 있는 회원의 정보를 띄워주도록 하자.

[ 만들 기능 ]
1. 내 블로그 클릭하면 내가 쓴 게시글 목록이 나오게 하기
2. 게시글 목록을 클릭하면 게시글 상세페이지로 이동
3. 위젯에 내 프로필 사진과 ID를 띄움
4. 세션 ID = 게시글 쓴 사람 이면 삭제버튼이 보이게 하기
5. 게시글 삭제 기능 구현

1. 메인페이지에서 내 블로그 클릭하면 내가 쓴 게시글 목록이 나오게 하기

간단하게 링크를 걸어준다.

1) JSP


  
<li class="nav-item"><a class="nav-link" href="myBlogAction.me?id=${sessionScope.id}">MyBlog</a></li>

로그인한 '내' 블로그에 가는 것이므로 sessionScope.id를 사용해준다.

2) Controller


  
//-------------------- 내 블로그 페이지를 보여주는 myBlogAction 페이지 생성 ---------------------------
else if (command.equals("/member/myBlogAction.me")) {
action = new MyBlogAction();
// MyBlogAction, 내 블로그 페이지 표시.
try {
forward = action.execute(req, resp);
System.out.println( "내 블로그 페이지 표시"))
}
catch (Exception e) {
e.printStackTrace();
}
}

3) Action

내 블로그에는 내가 쓴 게시글 목록을 띄울 것이다. sessionId가 있어야 내 블로그인 줄 알테니까 sessionId 생성 후 Service에 같이 던져준다.


  
package action;
import java.util.ArrayList;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import svc.MyPostListService;
import vo.ActionForward;
import vo.PostBean;
/* 인터페이스를 함께 추가했다. 내 블로그 표시를 위한 클래스이다.*/
public class MyBlogAction implements Action {
@Override
public ActionForward execute(HttpServletRequest req, HttpServletResponse resp) throws Exception {
//session을 써서 서버 생성함.
HttpSession session = req.getSession();
// 세션에서 id값 가지고 있기.
String sessionId = (String) session.getAttribute("id");
ActionForward forward = null; //어디로 갈지?
// 내 게시글 목록 및 내용 표시.
forward = new ActionForward();
MyPostListService myPostListService = new MyPostListService();
//배열로 게시글 목록을 가져온다. id는 같이 던져줘야 읽음.
ArrayList<PostBean> myPostList = myPostListService.getMyPostList(sessionId);
req.setAttribute("myPostList", myPostList);
//mainPage로 게시글 목록을 던져준다.
forward.setPath("./myBlog.jsp");
return forward;
}
}

4) Service

DB연결해서 커넥션 풀 열고 닫기


  
package svc;
import static db.JdbcUtil.close;
import static db.JdbcUtil.getConnection;
import java.sql.Connection;
import java.util.ArrayList;
import DAO.PostDAO;
import vo.PostBean;
/* 내 게시글를 띄울 시 DB와 JSP를 연결해주는 역할을 담당하는 클래스(Service) */
public class MyPostListService {
public ArrayList<PostBean> getMyPostList(String sessionId) { //리스트이므로 배열로 가져온다.
//커넥션 풀 : DB와 미리 연결해놓은 커넥션 객체들을 풀에 저장해뒀다가 클라에서 요청오면 객체 빌려주고 볼일 끝나면 다시 반납받아 풀에 저장하는 방식
Connection con = getConnection();
// DB와 연결을 얻어내고.
PostDAO postDAO = PostDAO.getInstance();
// DB접속용 DAO 객체 참조 얻어오기.(싱글톤 패턴 : 인스턴스가 1개만 생성됨)
// import static db.JdbcUtil.*;
postDAO.setConnection(con);
// 얻어온 커넥션 저장. -> 연결해서 할일 함.
ArrayList<PostBean> myPostList = postDAO.selectMyPostList(sessionId); //selectMyPostList는 PostDAO에서 받아온다.
//커넥션으로 DB에서 게시글 목록 받아서 배열에 저장
close(con);
// 사용을 마친 커넥션 반환
return myPostList;
// 내 게시글 목록 리턴.
}
}

5) DAO

내 블로그 게시글 목록을 가져온다. 목록이므로 미리보기처럼 보일 수 있게 게시글 내용은 15자 이내로 자르고, 회원 테이블에서 ID값과 pic을 가져와서 넣어준다.


  
//=========================== 내 블로그 게시글을 가져와 보여주는 SQL로직 ===============================
public ArrayList<PostBean > selectMyPostList(String sessionId) {
// ArrayList: 객체 배열 비슷, 컬렉션 프로임워크
// 여러 개의 게시글 정보를 저장한다.
//디비에 저장된 내 게시글 목록을 확인하는 SQL문(DB 이름 확인하기***)
String sql = "select p.*,m.mem_id,m.mem_pic from post_info p join memberinfo m on p.mem_no = m.mem_no where m.mem_id = ?"
ArrayList<PostBean> myPostList = new ArrayList<PostBean>();
PostBean pb = null; //Bean(vo)은 그릇이다. 뭘 가져올지는 vo에 목록이 있다.
try {
pstmt = con.prepareStatement(sql);
//세션 id값 가져와서 SQL 문에 넣어줌.
pstmt.setString(1, sessionId);
System.out.println( "sessionId : "+sessionId))
rs = pstmt.executeQuery(); //executeQuery : resultSet 객체 반환
//select는 executeQuery()를 사용한다.
//쿼리문 처리결과 ResultSet의 객체인 rs에 저장.
if (rs.next()) {
//조회된 결과가 있다면 아래 문장 수행.
do {
//한 번 수행하고 또 수행할 게 있으면 수행.
pb = new PostBean();
//게시글 1개의 정보를 저장할 수 있는 PostBean 객체 생성.
pb.setPOST_NO(rs.getInt( "POST_NO"))) //게시글번호
pb.setMEM_NO(rs.getInt( "MEM_NO"))) //회원번호
pb.setPOST_TITLE(rs.getString( "POST_TITLE"))) //게시글제목
pb.setPOST_THUMBNAIL(rs.getString( "POST_THUMBNAIL"))) //게시글섬네일
pb.setPOST_VIDEO(rs.getString( "POST_VIDEO"))) //게시글비디오
//--- 게시글 내용 가져와서 15자만 미리보기로 보여줌 ---
String preStr=rs.getString( "POST_CONTENT"))
if(preStr.length() > 30) {
preStr=preStr.substring(0,15)+ "..." //자르고 ... 붙이기
};
pb.setPOST_CONTENT(preStr); //게시글내용 미리보기
//----------------------------------------
pb.setVisit_cnt(rs.getInt( "Visit_cnt"))) //게시글조회수
pb.setPOST_UPLOADTIME(rs.getString( "POST_UPLOADTIME"))) //게시글업로드타임
//위젯에 띄울 내 회원정보
pb.setMEM_id(rs.getString( "MEM_ID"))) //게시글 쓴 회원 id
pb.setMEM_PIC(rs.getString( "MEM_PIC"))) //게시글 쓴 회원 사진
//조회된 결과를 PostBean객체에 저장.
myPostList.add(pb);
//저장하면서 생성된 것을 이제 List에 담아냄.(ArrayList)
//반복문이 실행될 때마다 게시글 1개씩 누적 시킴.
}
while (rs.next());
//rs.next때문에 어레이 리스트 다음 값으로 넘어간다.(차례차례 읽어옴)
}
}
catch (Exception ex) {
System.out.println( "selectMyPostList 에러 : " + ex))
}
finally {
close(rs);
close(pstmt);
}
return myPostList;
}

6) VO

vo에 있는 PostBean의 정보는 이전글 참고. 아래 두가지를 추가했다.


  
private String MEM_ID;
private String MEM_PIC;

결과창

2. 게시글 목록을 클릭하면 게시글 상세페이지로 이동

동일하게 상세보기 페이지로 연결해주면 된다. 그럼 상세보기 페이지이지만 위젯 내역은 똑같이 보이니 페이지가 이동하지 않은 상태로 내용만 바뀐 것처럼 보인다.


  
<a class="btn btn-primary" href="postViewAction.me?postViewAction.me?postno=${myPostList.POST_NO}">Read more →</a>

 

3. 위젯에 내 프로필 사진과 ID를 띄움

1번 항목 참조.

 

나만 내 게시글 삭제하기

 

4. 세션 ID = 게시글 쓴 사람 이면 삭제버튼이 보이게 하기

요걸 버튼으로 사용해보자.

세션 ID를 어떻게 가져올까 하다가...공부하는 겸 해서 자바스크립트 단에서 display : none 처리로 간단하게 해보기로 했다.

자바스크립트에 java 코드를 사용해본다. 서버 session에 있는 id값을 가져왔다.


  
<div class="text-muted fst-italic mb-2">${postObj.POST_UPLOADTIME}
by <b id="memId">${postObj.MEM_ID}</b></div>
<!--회원id를 b태그로 감싸서 id를 부여해줬다.-->
<script>
var sessionId = '<%=(String)session.getAttribute("id")%>';
var post_writer = document.getElementById('memId').innerText;
console.log(typeof(sessionId),typeof(post_writer)); //string,string
//세션 id가 포스트작성자가 아니면, 수정버튼과 삭제버튼 안보임.
if (sessionId != post_writer){
document.getElementById("post_update").style.display ='none';
document.getElementById("post_delete").style.display ='none';
console.log("내 게시글 아님. 수정 불가");
};
</script>

세션 아이디와 다름
세션 아이디와 같음

물론 좋은 방법은 아니다.

말 그대로 css로 감춘 것이기 때문에 스크립트에 노출되어 있으며, display : none 속성을 브라우저에서 없애주면 버튼이 보여지게 된다.

개발자 도구에서 버튼 발굴(?) 가능

하지만 이렇게 한 이유는 귀찮은 건 아니고 자바스크립트에서 세션값을 가져오는 방법을 사용해보기 위함이며, 수정 화면에 이동할 시, session에 있는 사용자가 수정권한이 있는지 컨트롤러 단에서 다시 확인할 것이기 때문이다.

컨트롤러 단에서 이걸 어떻게 받을까 고민하다가 postNo와 같이 get 방식으로 mem_id값도 보내주기로 했다.(2번 참고)

 

5. 게시글 삭제 기능 구현

삭제기능만 우선 구현해보자.

1) JSP

삭제버튼 클릭 시 해당 게시글 번호와 작성자를 쿼리스트링으로 보내줌


  
<a class="badge bg-secondary text-decoration-none link-light" href="postDeleteAction.me?writer=${postObj.MEM_ID}&postno=${postObj.POST_NO}" id="post_delete">삭제</a>

get 방식으로 보낼 때 파라미터에 작성자 id값도 함께 보내준다. 컨트롤러 단에서 확인할 것이기 때문!!

쿼리스트링을 보낼 때에는 &로 묶어서 보내준다.

2) Controller


  
//-------------------- 내 게시글을 삭제하는 postDeleteAction 페이지 생성 ---------------------------
else if (command.equals("/member/postDeleteAction.me")) {
action = new PostDeleteAction();
// PostDeleteAction, 삭제 후 페이지 표시.
try {
forward = action.execute(req, resp);
System.out.println( "게시글 삭제"))
}
catch (Exception e) {
e.printStackTrace();
}
}

3) Action

로그인 상태인지, 사용자가 게시글 작성자가 맞는지를 확인한다.

브라우저 alert를 띄우기 위해 resp.getWriter()를 이용해서 println으로 스크립트를 띄워주었다.

가져온 작성자 id값과 게시글 번호를 통해 분기한다.


  
package action;
import java.io.PrintWriter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import svc.PostDeleteService;
import vo.ActionForward;
public class PostDeleteAction implements Action {
@Override
public ActionForward execute(HttpServletRequest req, HttpServletResponse resp) throws Exception {
HttpSession session = req.getSession();
String id = (String) session.getAttribute("id");
// 로그인하면, id는 session에 저장되어 있다.
String post_writer = req.getParameter("writer");
int postNo = Integer.parseInt(req.getParameter("postno"));
System.out.println("writer"+post_writer);
ActionForward forward = null;
// 로그인 상태인지 확인 -> 아니면 로그인화면 고고
if (id == null) {
forward = new ActionForward();
forward.setRedirect(true);
forward.setPath("./memberLogin.me");
// 사용자가 작성자가 맞는지 확인 -> 아니면 alert를 띄우고 해당 포스트로 다시 이동
} else if (!id.equals(post_writer)) {
resp.setContentType("text/html;charset=UTF-8");
PrintWriter out = resp.getWriter();
out.println("<script>");
out.println("alert('작성자가 아닌 사람은 게시글을 삭제할 수 없습니다.')");
out.println("location.href='./postViewAction.me?postno="+postNo+"'");
out.println("</script>");
//사용자가 작성자면 삭제가 가능하다.
} else {
//Service에 삭제한 게시물 번호를 함께 넘겨준다.
PostDeleteService postDeleteService = new PostDeleteService();
boolean deleteResult = postDeleteService.deletePost(postNo);
// delete 하면 내 블로그로 이동
if (deleteResult) {
resp.setContentType("text/html;charset=UTF-8");
PrintWriter out = resp.getWriter();
out.println("<script>");
out.println("alert('삭제되었습니다.');");
out.println("location.href='./myBlogAction.me'");
out.println("</script>");
// delete 실패하면 alert 발생 후 해당 포스트로 다시 이동
} else {
resp.setContentType("text/html;charset=UTF-8");
PrintWriter out = resp.getWriter();
out.println("<script>");
out.println("alert('삭제 실패');");
out.println("location.href='./postViewAction.me?postno="+postNo+"'");
out.println("</script>");
}
}
return forward;
}
}

4) Service

DB연결해서 커넥션 풀 열고 닫기. 게시글 번호는 계속 가지고 간다.


  
package svc;
import static db.JdbcUtil.close;
import static db.JdbcUtil.commit;
import static db.JdbcUtil.getConnection;
import static db.JdbcUtil.rollback;
import java.sql.Connection;
import DAO.PostDAO;
public class PostDeleteService {
public boolean deletePost(int postNo) {
boolean deleteResult = false;
Connection con = getConnection();
PostDAO postDAO = PostDAO.getInstance();
postDAO.setConnection(con);
int deleteCount = postDAO.deletePost(postNo);
//지우면 지웠다고 결과 1 리턴 아니면 롤백
if (deleteCount > 0) {
commit(con);
deleteResult = true;
} else {
rollback(con);
}
close(con);
return deleteResult;
}
}

5) DAO

내 블로그 게시글 목록을 가져온다. 목록이므로 미리보기처럼 보일 수 있게 게시글 내용은 15자 이내로 자르고, 회원 테이블에서 ID값과 pic을 가져와서 넣어준다.


  
//=========================== 게시글을 삭제하는 SQL로직 ===============================
// PostDeleteService에서 게시글 삭제 시 DB와 JSP를 연결할 때 인자로 쓰임.
public int deletePost(int postNo) {
//우클릭 Create method
// 해당 게시글을 삭제하는 SQL문(DB 이름 확인하기***)
String sql = "delete from post_info where post_No=?"
int deleteCount = 0; //삭제 유무
try {
pstmt = con.prepareStatement(sql);
pstmt.setInt(1, postNo); //가져온 게시글 번호 보내주기
pstmt.executeUpdate();
deleteCount = 1;
}
catch(Exception ex) {
System.out.println( "삭제 안됨"))
deleteCount = 0;
}
finally {
close(rs);
close(pstmt);
}
return deleteCount;
}

6) VO

vo에 있는 PostBean의 정보는 이전글 참고.

결과창 : 유저가 자기 게시글을 삭제함

css로 버튼을 감췄기 때문에 display:none을 풀어서(?) 삭제 버튼을 활성화시킬 수도 있다. 이 때 alert가 발생하면서 다시 해당 페이지로 이동한다.

결과창 : 유저가 다른사람 게시글을 삭제할 경우 alert 발생

 

 

▼ 진행중인 gitHub 링크를 남깁니다.

 

GitHub - k-haein/BlogPrj: 회원가입/로그인/게시글 등록 등의 블로그를 처음부터 만듭니다.

회원가입/로그인/게시글 등록 등의 블로그를 처음부터 만듭니다. Contribute to k-haein/BlogPrj development by creating an account on GitHub.

github.com

 

반응형
LIST
반응형
SMALL

jQuery 를 이용하여 id, class, name 의 input value 값 가져오기


  
<input type="text" id="inputId" class="inputClass" name="inputName">

 

1) id 값 기준으로 가져 오기


  
var valueById = $('#inputId').val();

# 은 아이디를 의미

2) class 값 기준으로 가져 오기


  
var valueByClass = $('.inputClass').val();

. 은 클래스를 의미

3) name 값 기준으로 가져 오기


  
var valueByname = $('input[name=inputName]').val();

input 태그의 name값을 그대로 입력

반응형
LIST
반응형
SMALL

Servlet

클라이언트 요청을 처리하고, 그 결과를 반환하는 Servlet 클래스의 구현 규칙을 지킨 자바 웹 프로그래밍 기술. 즉, JAVA를 사용해서 웹을 만들기 위해 필요한 기술로, 자바 코드 안에 HTML 포함되어 있다. 클라이언트가 어떤 요청을 하면 그 결과를 다시 전송해주는 역할을 하는 JAVA 프로그램이다.

JSP

JavaServer Pages, HTML 코드에 JAVA 코드를 넣어 동적인 웹페이지를 생성하는 웹어플리케이션 도구. JSP가 실행되면 자바 서블릿(Servlet)으로 변환되며 웹 어플리케이션 서버(WAS)에서 동작되면서 필요한 기능을 수행하고 그렇게 생성된 데이터를 웹페이지와 함께 클라이언트로 응답한다.

서블릿 이전에 CGI가 있었다.

서블릿의 조상인 CGI(Common Gateway Interface)라는게 있다. CGI 이전의 웹서버는 단순히 사용자가 특정 경로를 입력하면 그 경로에 해당하는 리소스만 뿌려주었다. (주소 입력하면 정적인 페이지로 이동만 시켜줌) 그런데 CGI가 나오면서 사용자가 입력값을 인자로 넣은 프로그램을 실행시키면 그 결과를 웹으로 뿌려줄 수 있게 되었다. 바야흐로 동적인 페이지를 보여주기 위해 임의의 프로그램을 실행할 수 있는 기술의 탄생이다.

CGI 이전엔 경로->정적페이지 출력만 가능했는데,
CGI는 사용자에게 입력값을 받아 동적인 페이지를 보여줄 수 있게됨.

정적인 페이지는 예를들어 기업설명 페이지, 즉 사용자에 의해 변경될 일이 없으며 똑같 내용의 페이지를 말하고, 동적인 페이지는 SNS 페이지와 같이 사용자의 좋아요와 팔로우에 따라 알고리즘을 변경하여 사용자마다 다른 페이지를 보여주는 것을 말한다.

CGI 이전까지는 말 그대로 글자와 이미지 출력이 다였지만, CGI의 탄생으로 주소 하나에도 다양한 내용을 각자의 사용자에게 보여줄 수 있게 되었다.

CGI에서 서블릿으로

CGI는 웹서버에 요청이 들어오면 각각 처리한다. 일하는 인력 많고 처리를 하나씩 맡아서 한다.

CGI의 동작방식

CGI는 브라우저에서 웹서버에 요청이 갔을 때, 각각에 대한 처리 프로세스를 만들어낸다는 단점이 있었다. 그래서 대량의 트래픽을 처리하기에는 약간 무리가 있었다.

서블릿의 동작방식

서블릿도 CGI 규칙에 따라 데이터를 주고 받는 것은 같다. 하지만 서블릿은 각 요청에 대한 프로세스를 생성하는 것이 아니라, 서블릿 컨테이너로 보내서 처리하도록 했다.


서블릿 컨테이너는 요청이 들어오면 해당 요청을 처리할 일꾼 서블릿을 설정파일(가이드문서 같은거)을 읽어서 찾은 뒤, 어떤 서블릿이 필요한지 알았으면 해당 일꾼 서블릿을 찾는다. 또한 서블릿 인스턴스(클래스를 구체화한 제품)가 있다면 그 인스턴스를 가져와서 사용하고, 없으면 생성해서 그걸 이 일꾼 서블릿에게 가져다준다.


이 서블릿 인스턴스를 요청객체 HttpServletRequest와 응답 객체 HttpServletResponse에게 인자로 넘겨주면, 해당 일꾼 서블릿이 요청에 따른 로직을 처리하고, 처리 결과를 HttpServletResponse 객체에 담아 반환한다.(일꾼은 일을 끝냈음)


처리를 마치면 요청, 응답객체들은 사라지고 사용했던 서블릿 인스턴스만 남는다. 이 서블릿 인스턴스는 소멸하지 않고 있다가 다음에 같은 요청이 들어오면 서블릿 컨테이너가 다시 일꾼 서블릿에게 보내주면서 금방금방 일처리를 해준다.

 

서블릿 컨테이너는 서블릿의 생명주기를 관리하는 객체이다. 생성, 호출, 적절한 시점의 소멸을 담당한다.

 

그럼 한 요청을 처리하는 중에 또다른 요청이 들어오면 어떻게 될까? 그럼 멀티스레드로 요청이 처리되면서 일꾼 서블릿이 여러군데에서 호출되서 여러 스레드에서 사용된다. (이곳저곳 불려간다는 말임) 문제는 스레드 생성도 비용이 많이 들고, 다른 스레드로 전환하는 것도 부하가 걸리기 때문이다.

 

따라서 WAS는 이러한 문제를 처리하기 위해 프로세스 1개가 있고 그 내부에 스레드 풀이라는 스레드들이 생성될 수 있는 공간을 만들어 스레드로 처리했다.


스레드 풀은 스레드를 미리 생성해서 스레드가 필요할 때 꺼내쓰고 반납하는 저장소이다. 개수를 제한해서 일정 이상의 요청이 들어왔을 때는 대기하도록 만들어서 대량의 요청이 들어왔을 때도 조금 더 안정성이 증가했다.
그리고 사실 비슷한 경로에 요청을 한 클라이언트들의 비즈니스 로직은 대부분 중복되는 부분이므로, 스레드 사이에 있는 공유 자원 (Code, Data, Heap)을 통해 더욱 효율적인 처리가 가능했다.

 

 

▼ 스레드와 메모리에 관한 자세한 설명이 궁금하다면?

서블릿 개발 흐름

서블릿 라이프 사이클

서블릿 라이프 사이클

  1. 서블릿은 생성 이후에 init() 메소드를 통해 초기화된다.
  2. 클라이언트가 서비스(service())로 보낸 요청들을 처리한다.
  3. 서블릿은 사용 중지된 후, destroy()를 이용해 제거된다.

서블릿 컨테이너가 서블릿 클래스를 로딩하고, 인스턴스를 생성해 생성자와 함께 자바 서블릿 객체를 생성한다. 이 후 초기화와 사용자의 요청 비지니스 로직 처리(service), 완료 후 서블릿 상태 종료 및 소멸을 서블릿 컨테이너가 담당한다.

 

이 모든 과정을 컨테이너 내부에서 스레드 단위로 요청을 처리하는데, 이 과정을 개발자가 아닌 컨테이너가 제어하는 것을 IoC (Inversion of Control)라고 한다.

반응형
LIST
반응형
SMALL

+ Recent posts

반응형
LIST