글검색() 의 결과가 저장된 boards 를 setAttribute 하여 dispatcher 를 이용해 list페이지로 이동하게 한다.
글검색 된 boards 도 페이지처리가 적용되어야 하기 때문에 글개수(keyword) 함수를 오버로딩하여 새로 만들어야 한다.
BoardService.java
public List<Board> 글검색(String keyword, int page){
return boardDao.findByKeyword(keyword, page);
}
public int 글개수(String keyword) {
return boardDao.count(keyword);
}
BoardDao.java
public List<Board> findByKeyword(String keyword, int page){
String sql = "SELECT * FROM board WHERE title like ? ORDER BY id DESC LIMIT ?, 4"; // 0,4 4,4 8,4
Connection conn = DB.getConnection();
PreparedStatement pstmt = null;
ResultSet rs = null;
List<Board> boards = new ArrayList<>();
try {
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, "%"+keyword+"%");
pstmt.setInt(2, page*4); // 0 -> 0, 1 ->4, 2->8
rs = pstmt.executeQuery();
// Persistence API
while(rs.next()) { // 커서를 이동하는 함수
Board board = Board.builder()
.id(rs.getInt("id"))
.title(rs.getString("title"))
.content(rs.getString("content"))
.readCount(rs.getInt("readCount"))
.userId(rs.getInt("userId"))
.createDate(rs.getTimestamp("createDate"))
.build();
boards.add(board);
}
return boards;
} catch (Exception e) {
e.printStackTrace();
} finally { // 무조건 실행
DB.close(conn, pstmt, rs);
}
return null;
}
public int count(String keyword) {
String sql = "SELECT count(*) FROM board WHERE title like ?";
Connection conn = DB.getConnection();
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, "%"+keyword+"%");
rs = pstmt.executeQuery();
if(rs.next()) {
return rs.getInt(1);
}
} catch (Exception e) {
e.printStackTrace();
} finally { // 무조건 실행
DB.close(conn, pstmt, rs);
}
return -1;
}
public int deleteById(int id) { // 회원가입
String sql = "DELETE FROM board WHERE id = ?";
Connection conn = DB.getConnection();
PreparedStatement pstmt = null;
try {
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, id);
int result = pstmt.executeUpdate();
return result;
} catch (Exception e) {
e.printStackTrace();
} finally { // 무조건 실행
DB.close(conn, pstmt);
}
return -1;
}
BoardService.java
public int 글삭제(int id) {
return boardDao.deleteById(id);
}
CommonRespDto.java
모든 요청,응답을 이 통합Dto 하나로 관리합니다.
제네릭을 사용하네요.
package com.cos.blog.domain.board.dto;
import lombok.Data;
@Data
public class CommonRespDto<T> {
private int statusCode; // 1, -1
private T data;
}
BoardController.java
else if(cmd.equals("delete")) {
int id = Integer.parseInt(request.getParameter("id"));
// DB에서 id값으로 글 삭제
int result = boardService.글삭제(id);
// 응답할 json 데이터를 생성
CommonRespDto<String> commonRespDto = new CommonRespDto<>();
commonRespDto.setStatusCode(result);
commonRespDto.setData("성공");
Gson gson = new Gson();
String respData = gson.toJson(commonRespDto);
System.out.println("respData : "+respData);
PrintWriter out = response.getWriter();
out.print(respData);
out.flush();
}
CommonRespDto 는 알아두면 편리한데 제가 아직 개념정리가 덜되어있어서 설명이 어렵습니다.
원래 지원하던 다음블로그 ? 에디터가 좋았는데 Flash 가 2021년에 지원중단되면서 티스토리가 지원하는 신 에디터로 글을 써야하거든요
신 에디터 사진넣는거, 이건 좋ㄷ ㅏ이말이야. 근데 글자 폰트조절을 못하겠다는거 >> 화난다 .
ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
글 목록 보기를 구현해봅시다.
'글 목록' 을 볼려면 글이 저장되어있는 데이터베이스를 Select 해야할것입니다.
데이터베이스가 어떻게 생겼었는지 다시 면상이나 한 번 보죠 ~
사실 제가 글쓰는 텀이 너무 길어서 까먹었습니다.
MySQL
create user 'bloguser'@'%' identified by '비밀번호';
GRANT ALL PRIVILEGES ON *.* TO 'bloguser'@'%';
create database blog;
use blog;
drop table user;
drop table board;
drop table reply;
CREATE TABLE user(
id int primary key auto_increment,
username varchar(100) not null unique,
password varchar(100) not null,
email varchar(100) not null,
address varchar(100),
userRole varchar(20),
createDate timestamp
) ;
CREATE TABLE board(
id int primary key auto_increment,
userId int,
title varchar(100) not null,
content longtext,
readCount int default 0,
createDate timestamp,
foreign key (userId) references user (id)
);
CREATE TABLE reply(
id int primary key auto_increment,
userId int,
boardId int,
content varchar(300) not null,
createDate timestamp,
foreign key (userId) references user (id) on delete set null,
foreign key (boardId) references board (id) on delete cascade
);
우리가 봐야할 데이터는 Board .
BoardDao.java
public List<Board> findAll(){
String sql = "SELECT * FROM board ORDER BY id DESC";
Connection conn = DB.getConnection();
PreparedStatement pstmt = null;
ResultSet rs = null;
List<Board> boards = new ArrayList<>();
try {
pstmt = conn.prepareStatement(sql);
rs = pstmt.executeQuery();
// Persistence API
while(rs.next()) { // 커서를 이동하는 함수
Board board = Board.builder()
.id(rs.getInt("id"))
.title(rs.getString("title"))
.content(rs.getString("content"))
.readCount(rs.getInt("readCount"))
.userId(rs.getInt("userId"))
.createDate(rs.getTimestamp("createDate"))
.build();
boards.add(board);
}
return boards;
} catch (Exception e) {
e.printStackTrace();
} finally { // 무조건 실행
DB.close(conn, pstmt, rs);
}
return null;
}
BoardDao에 findAll() 함수 추가.
DB에서 Board를 SELECT 해서 싸ㅡ악 가져오네요
BoardService.java
public List<Board> 글목록보기(){
return boardDao.findAll();
}
Ajax (Async Javascript And XML)는 웹 페이지에서 새로운 데이터를 보여주려고 할 때 웹페이지 전체를 새로고침 하지 않고, 보여주고자 하는 데이터가 포함된 페이지의 일부 만을 로드 하기 위한 기법입니다.
Ajax는 비동기 처리 모델 (또는 non-blocking 이라고도 함)을 사용하여 데이터를 처리합니다.
동기 처리 모델에서 브라우저는 자바스크립트 코드를 만나면 스크립트를 처리하기 전까지 다른 작업을 일시 중지하고, 자바스크립트 코드의 처리가 끝난 후 기존 작업을 진행합니다.
반면에 Ajax를 사용하면 브라우저는 서버에 데이터를 요청한 뒤 페이지의 나머지를 로드하고 페이지와 사용자의 상호작용을 처리합니다.
Ajax 동작방식
function usernameCheck(){
// DB에서 확인해서 username이 없다면 isChecking = true
var username = $("#username").val();
$.ajax({
type: "POST",
url: "/blog/user?cmd=usernameCheck",
data: username,
contentType: "text/plain; charset=utf-8",
dataType: "text" // 응답 받을 데이터의 타입을 적으면 자바스크립트 오브젝트로 파싱해줌.
}).done(function(data){
if(data === 'ok'){ // 유저네임 있다는 것
isChecking = false;
alert('유저네임이 중복되었습니다.')
}else{
isChecking = true;
$("#username").attr("readonly", "readonly");
alert("해당 유저네임을 사용할 수 있습니다.")
}
});
}
//UserController.java (Servlet)
else if(cmd.equals("usernameCheck")) { //유저네임 중복체크
BufferedReader br = request.getReader(); //input 에 입력된 username을 읽음
String username = br.readLine();
System.out.println(username);
//DB와 통신함 (service -> Dao)
//Dao 에서 Select 해보고 int 결과값을 리턴해줌. 있으면 1 없으면 not 1
int result = userService.유저네임중복체크(username);
PrintWriter out = response.getWriter();
if(result == 1) {
out.print("ok");
}else {
out.print("fail");
}
out.flush();
}
요청(request) - 브라우저가 서버에 정보를 요청한다. 바로 위에있는 JoinForm.jsp 에서 첫번째 Form의 중복검사 action이 usernameCheck() 함수를 가지고있는데, 그 함수는 파일 하단에. 자바스크립트 함수로 정의되어 있습니다.
이 함수가 JQuery를 이용한 Ajax 요청방법입니다. ( Web Front 에 입력된 데이터를 서버로 전송 ) type : 요청방식 (get, post, delete, put) // url : 요청주소 ( Controller 에서 Servlet이 처리함 )
data : 서버로 보낼 데이터. (위 함수에서는 var username = $("#username").val() 이 전송되는중)
contentType : 응답받을 데이터의 형태, 보통은 데이터를 처리하기위해 JSON 이겠지만 Controller의 결과값을 보면 ok , fail 의 단순 문자열이기때문에 text
응답에 성공을 한다면, done(function(data){ '대충 실행될 명령' } ) 분기로 가서 응답받은 데이터( data << Ajax 함수의 data 아님. Ajax의 결과값임 )로 해야할 일을 처리합니다. data를 result 로 바꾸는게 좀 보기 편할거같네요
서버의 동작 - 서버는 JSON, XML 등의 형식으로 데이터를 전달한다. 최근에는 JSON을 가장 많이 사용하고 있습니다.
package com.cos.blog.service;
import com.cos.blog.domain.user.User;
import com.cos.blog.domain.user.UserDao;
import com.cos.blog.domain.user.dto.JoinReqDto;
import com.cos.blog.domain.user.dto.LoginReqDto;
import com.cos.blog.domain.user.dto.UpdateReqDto;
public class UserService {
private UserDao userDao;
public UserService() {
userDao = new UserDao();
}
public int 회원가입(JoinReqDto dto) {
int result = userDao.save(dto);
return result;
}
public User 로그인(LoginReqDto dto) {
return userDao.findByUsernameAndPassword(dto);
}
public int 회원수정(UpdateReqDto dto) {
return -1;
}
public int 유저네임중복체크(String username) {
int result = userDao.findByUsername(username);
return result;
}
}
drop table user;
drop table board;
drop table reply;
CREATE TABLE user(
id int primary key auto_increment,
username varchar(100) not null unique,
password varchar(100) not null,
email varchar(100) not null,
address varchar(100),
userRole varchar(20),
createDate timestamp
) ;
CREATE TABLE board(
id int primary key auto_increment,
userId int,
title varchar(100) not null,
content longtext,
readCount int default 0,
createDate timestamp,
foreign key (userId) references user (id)
);
CREATE TABLE reply(
id int primary key auto_increment,
userId int,
boardId int,
content varchar(300) not null,
createDate timestamp,
foreign key (userId) references user (id) on delete set null,
foreign key (boardId) references board (id) on delete cascade
);
Model은 무엇을 할지 정의합니다. 비지니스 로직에서의 알고리즘, 데이터 등의 기능을 처합니다. Controller는 어떻게 할지를 정의합니다. 요청을 받아서 화면과 Model과 View를 연결시켜주는 역할을 하지요. View는 무엇을 화면으로 보여주는 역할을 하지요. 웹이라면 웹페이지, 모바일이라면 어플의 화면의 보여지는 부분입니다.
용어 설명2
Model: 어플리케이션의 데이터, 자료를 의미합니다. View: 사용자에게 보여지는 부분, 즉 유저 인터페이스(User interface)를 의미합니다.
프로그램에 사용되는 데이터를 의미하며 데이터베이스(DB), 상수, 문자열과 같은 변수들, 비전 프로그램이라면 카메라 정보와 같은 것들이 해당됩니다. 모델에는 뷰나 컨트롤러의 정보가 전혀 없습니다. 단지, 정보만 반환하거나 설정할 수 있습니다.
뷰(View)
다이얼로그에 존재하는 텍스트박스, 라벨, 버튼 등 사용자 인터페이스(User interface) 요소들을 의미합니다. 사용자가 제어하고 데이터를 확인할 수 있는 영역입니다. 뷰에서는 별도의 데이터를 보관하지 않습니다. 뷰에서 입력받고 출력해주는 모든 데이터는 모델을 사용해야합니다.
컨트롤러(Controller)
모델과 뷰를 관장하는 브릿지(Bridge)역할을 수행합니다. 사용자가 버튼을 클릭하면 이벤트는 뷰에서 발생하지만 내부 처리는 컨트롤러에서 관리하는 것입니다. 또한,입력이 발생하면 이에 대한 통지를 담당합니다.
용어 설명4
M (model, domain)
M은 Model을 가리킨다. Model이란 프로그램이 작업하는 세계관의 요소들을 개념적으로 정의한 것이라고 볼 수 있다. 예를 들어 음식점 무인 포스기를 개발한다고 가정해보자. 무인포스기가 정상적으로 목표하는 작업을 수행하기 위해서는 우선 메뉴가 있어야하고, 메뉴를 담을 수 있는 장바구니, 해당 메뉴의 수량, 결제수단, 할인정책 등등이 필요할 것이다.
이처럼 프로그램이 목표하는 작업을 원활하게 수행하기 위해 필요한 물리적 개체, 규칙, 작업등의 요소들을 구분되는 역할로써 정의해놓은게 Model이 된다. Model은 DTO와 DAO로 분류할 수 있다. 두 개념에 대해서는 나중에 다른 세션에서 정리할 예정이므로 간단히 언급만하고 넘어가겠다.
결과적으로 Model을 잘 설계하는 것은, 해당 도메인 세계를 얼마만큼 이해하고 있는지와도 밀접한 연관이 있다. 꼭 물리적인 요소뿐만아니라 추상적인 요소 또한 해당 작업을 수행하는데 특정 책임과 역할로서 구분될 수 있다면 최대한 구체적이고 작은 entitiy를 유지하면서 Model을 설계하는 것이 중요하다.
V (view)
V는 View를 가리키고 사용자가 보는 화면에 입출력 과정 및 결과를 보여주기 위한 역할을 한다. 입출력의 순서나 데이터 양식은 컨트롤러에 종속되어 결정되고, 도메인 모델의 상태를 변환하거나, 받아서 렌더링하는 역할을 한다.
view를 구현할 때 주의할 점은 도메인 로직의 어떤 것도 알고 있으면 안된다는 것이다. 절대적으로 객체를 전달받아 상태를 바로 출력하는 역할만을 담당해야 한다. 그렇기 때문에 view에서는 도메인 객체의 상태를 따로 저장하고 관리하는 클래스 변수 혹은 인스턴스 변수가 있을 필요가 없다.
C (controller)
C는 Controller를 가리킨다. controller는 model과 view를 연결 시켜주는 다리 역할을 함과 동시에 도메인 객체들의 조합을 통해 프로그램의 작동 순서나 방식을 제어한다. controller는 view와 model이 각각 어떤 역할과 책임이 있는 지 알고 있어야 한다.
웹 프로그래밍에서는 Controller에서 service layer를 분리하여 domain 로직이 수행되는 곳과 view의 요청을 매핑하는 곳을 독립적으로 관리할 수 있다.
웹사이트 개발시 MVC 패턴을 적용하는 일반적인 목적
- 애플리케이션에 데이터 구조와 Model(비지니스 로직)은 전형적으로 자주 변경되지 않지만, 데이터의 View(프레젠테이션 로직)은 사실 자주 변경되기 때문에 Model(비즈니스 로직)과 View(프레젠테이션 로직)을 분리함으로써 이러한 요구의 변경에 따라 애플리케이션에 유지보수가 쉬워진다.
예를 들어 웹사이트가 보여주는 데이터 구조에는 변함이 없지만, 국내외의 사용자에게 다양한 언어로 데이터를 보여주어야 할 경우 또는 PC/스마트폰/스마트TV 등과 같은 해상도가 다른 장치에서 동일한 데이터를 보여주어야 할 경우 Model(비즈니스 로직)은 유지하고, View(프레젠테이션 로직)만 수정하면 된다. 즉 MVC 패턴을 사용하면 웹사이트의 유지보수가 쉽다.
- Model(비지니스 로직)을 담당하는 웹 개발자와 View(프레젠테이션 로직)을 담당하는 웹 디자이너 분리된 작업이 가능하다.
추가 설명
JSP 에서 JAVA로 데이터를 전송하려면 그냥 form action="" 을 이용하면 되지만