[04]웹 게시판(CRUD)_1차
-순서 정리 : https://exploreryun.tistory.com/315?category=1004845-
-수업 자료 참고-
https://exploreryun.tistory.com/309
- 클론코딩(정리 버전) -
(+) 보드 리스트 변동되는 것 수정
//기존
<h2>BoardList(${boardCount })</h2><!-- 레코드 수 변동 -->
//바꾼 것
<h2>BoardList(${count })</h2><!-- 레코드 수 변동 -->
※ 웹 게시판 수업에서 할 것들 ※
-전체 게시판 구조
-기본적인 CRUD 데이터베이스 연결
-리스트(list) 하나 완성하고 그 다음에 업데이트/인서트/딜리트 만들 것
인서트, 상세보기, 딜리트 등등 // 뷰 ← 컨트롤러 ← 서비스 ← 디비
-페이징 기능, 검색 기능 → 1차 게시판 완료
-주어진 코드의 설정 변경해보는 연습
-2차 게시판 : 조회수, 댓글, 검색 기능 붙이기
Rest Controller 사용(값을 바로 리턴할 필요가 있을 때만 사용)
*CRUD : Create(생성), Read(읽기), Update(갱신), Delete(삭제)
*Rest : 댓글 영역, 회원가입에서 아이디 중복검사할 때, 안드로이드 데이터 전송할 때
(외부 어딘가에서 외부로 데이터 전송할 때?)
*AOP : 로고 찍어본다거나, 보안작업할 때 사용. 오류 체킹. 하나 만들어서 필요한 곳에 끼웠다 뺐다할 수 있어서 편함. 트랜잭션할 때 많이 사용.
1. 프로젝트 구성
(1) 구조
일반적 웹 프로젝트 |
Presentation | Business | Persistence tier | |
스프링 MVC | Spring MVC | Spring Core | MyBatis | DB |
└데이터 처리하는 법 : 클래스를 가지고 DB처리하도록 하는 법은 스프링부터에서 할 것.
(2) 영역 네이밍 규칙
-Controller | -Service | -ServiceImpl |
Controller 클래스 | 비즈니스 영역 담당하는 인터페이스 | 인터페이스를 구현한 클래스 |
-DAO | -Repository | VO |
Data Access Object | 저장소 | Read Only 목적 데이터 불변 설계 |
└DAO나 Repository는 보편적으로 영역을 따로 구성. 수업에선 별도의 DAO 대신 MyBatis의 Mapper 인터페이스 활용
└DTO : 주로 데이터 수집 용도 (*VO, DTO : 테이블 데이터. entity)
(3) 패키지 구성
-기본적으로 3단계 구성 : ex) com.wsy.web_board : 도메인.그룹아이디.프로젝트이름
com.wsy.web_board.config | com.wsy.web_board.controller | com.wsy.web_board.service |
프로젝트 관련된 설정 클래스 | MVC Controller | Service 인터페이스, 구현 클래스 |
com.wsy.web_board.domain | com.wsy.web_board.persistence | com.wsy.web_board.exception |
entity, VO 같은 거 | Mapper. 디비 연결하는 것 | 예외처리 |
com.wsy.web_board.aop | com.wsy.web_board.security | com.wsy.web_board.util |
AOP 관련 | Security 관련 | 입시 작업하는 클래스, 함수 모아두는 클래스 등 두는 곳 |
(4) CRUD 흐름
게시물 목록(list) → 등록 → 결과 → 목록 →게시물 조회 : 수정/삭제
(5) 환경 설정_위 작업을 하기 위해 해야 할 것들
(5-1) Legacy Project 생성
(5-2) pom.xml 설정
-버전 변경 : Java, Spring, junit, Servlet
-maven repository에서 필요한 것들 코드 복사해서 <dependencies> 태그 안에 붙여넣기
javax.ervlet / spring-test / spring jdbc / spring tx / hikariCP / myBatis / myBatis spring / log4jdbc / lombok
*간단 설명*
-LomBok : 필드, 생성자, 접근자/설정자를 생성하지 않아도 @Annotation만 적어주면 자동으로 생성
-디비 연결을 위해 : spring-jdbc(드라이버 로딩), junit과 spring-test(디비 연결 테스트)
-Log4jdbc : 디비에 작업한 내용을 전부 콘솔에 찍어주는 것. 개발용 드라이버. 데이터가 제대로 오는지 확인
-MyBatis : 연결만 하면됨.
-커넥션 풀 : 커넥션 만들어서 풀에 넣음. 다른 사람이 사용할 때 연결만 하면 되도록. - HikariCP
(원래-jsp는 커넥션 사용할 때, 한번 사용하고 종료해야 했음. close(); )
-spring-tx : 트랜잭션 처리할 때 사용
(5-3) 데이터 입력 및 테스트
오라클/MySQL로 테이블 생성 및 데이터 입력 후 STS4에서 디비 연결해 데이터 가져와 출력
정상적으로 연결됐고 데이터를 가져오는지 확인하기 위해서 junit 사용
(*디비 : 오라클, MySQL, 몽고 디비 등 다양한 디비 프로그램이 있으므로 연결방법도 다양)
(5-4) JDBC 드라이버 프로젝트 내 추가
2. Project
(1) 레거시 프로젝트 생성
-프로젝트 이름 : boardSystem01
-top level package name : com.wsy.boardsystem01
(2) 환경 설정
(2-1) pom.xml
-자바 버전 변경(11), spring mvc web(릴리즈 최신버전 사용 5.2.19 RELEASE), maven-eclipse-plugin(Eclipse Enterprise Java and Web Developer Tools 3.24), javax.servlet(javax.servlet-api 3.1.0), junit(4.13)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.wsy</groupId>
<artifactId>boardsystem00</artifactId>
<name>boardSystem00</name>
<packaging>war</packaging>
<version>1.0.0-BUILD-SNAPSHOT</version>
<properties>
<!-- ★ Java 버전 변경 -->
<java-version>11</java-version>
<!-- ★ maven repository에서 버전 확인 후 수정. 최신 RELEASE -->
<org.springframework-version>5.2.19.RELEASE</org.springframework-version>
<org.aspectj-version>1.6.10</org.aspectj-version>
<org.slf4j-version>1.6.6</org.slf4j-version>
</properties>
<dependencies>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${org.springframework-version}</version>
<exclusions>
<!-- Exclude Commons Logging in favor of SLF4j -->
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<!-- AspectJ -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${org.aspectj-version}</version>
</dependency>
<!-- Logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${org.slf4j-version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>${org.slf4j-version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${org.slf4j-version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.15</version>
<exclusions>
<exclusion>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
</exclusion>
<exclusion>
<groupId>javax.jms</groupId>
<artifactId>jms</artifactId>
</exclusion>
<exclusion>
<groupId>com.sun.jdmk</groupId>
<artifactId>jmxtools</artifactId>
</exclusion>
<exclusion>
<groupId>com.sun.jmx</groupId>
<artifactId>jmxri</artifactId>
</exclusion>
</exclusions>
<scope>runtime</scope>
</dependency>
<!-- @Inject -->
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
<!-- Servlet -->
<!-- ★ javax servlet 추가 -->
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- Test -->
<dependency>
<groupId>junit</groupId><!-- ★ 버전만 변경 -->
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
<!-- ★★★ 추가한 것들 -->
<!-- 스프링 관련된 것은 스프링프레임워크 버전이랑 동일. 5.2.19 RELEASE -->
<!-- spring-test -->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.19.RELEASE</version>
<scope>test</scope>
</dependency>
<!-- Spring JDBC -->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.19.RELEASE</version>
</dependency>
<!-- spring tx -->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-tx -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.2.19.RELEASE</version>
</dependency>
<!-- hikariCP :최신버즌 -->
<!-- https://mvnrepository.com/artifact/com.zaxxer/HikariCP -->
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>5.0.1</version>
</dependency>
<!-- mybatis3.5.5 미리 넣어둘 것. -->
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.5</version>
</dependency>
<!-- MyBatis Spring -->
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.7</version>
</dependency>
<!-- Log4Jdbc Log4j2 JDBC 4 1 : 1.16 -->
<!-- https://mvnrepository.com/artifact/org.bgee.log4jdbc-log4j2/log4jdbc-log4j2-jdbc4.1 -->
<dependency>
<groupId>org.bgee.log4jdbc-log4j2</groupId>
<artifactId>log4jdbc-log4j2-jdbc4.1</artifactId>
<version>1.16</version>
</dependency>
<!-- lombok -->
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
<scope>provided</scope>
</dependency>
<!-- End -->
</dependencies>
<build>
<plugins>
<plugin>
<!-- ★ 설치한 플러그인 버전 입력. Eclipse Enterprise Java and Web Developer Tools 3.24 -->
<artifactId>maven-eclipse-plugin</artifactId>
<version>3.24</version>
<configuration>
<additionalProjectnatures>
<projectnature>org.springframework.ide.eclipse.core.springnature</projectnature>
</additionalProjectnatures>
<additionalBuildcommands>
<buildcommand>org.springframework.ide.eclipse.core.springbuilder</buildcommand>
</additionalBuildcommands>
<downloadSources>true</downloadSources>
<downloadJavadocs>true</downloadJavadocs>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.5.1</version>
<configuration>
<!--maven comiler plugin 버전 변경-->
<source>11</source>
<target>11</target>
<compilerArgument>-Xlint:all</compilerArgument>
<showWarnings>true</showWarnings>
<showDeprecation>true</showDeprecation>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.2.1</version>
<configuration>
<mainClass>org.test.int1.Main</mainClass>
</configuration>
</plugin>
</plugins>
</build>
</project>
(1-3) Lombok - 사용하려면 설치 필요
LomBok : 필드, 생성자, 접근자/설정자를 생성하지 않아도 @Annotation만 적어주면 자동으로 생성해주는 것.
프로젝트가 커지만 코드가 복잡. 코드 간결화를 위해 Annotation만 해주면 자동 생성해줌.
-다운로드_https://projectlombok.org/ >> Download 1.18.22 >> 다운받은 파일(.jar)
>> sts-4.13.1.RELEASE 폴더 안에 옮기기 >> 실행하면, 사용할 이클립스/STS4를 넣는 작업
>> STS4 선택해서 인스톨/업데이트 >> STS 재부팅 >> maven project에서 코드 복사해서 pom.xml에 넣어줘야 함.
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
<scope>provided</scope>
</dependency>
>> 확인 작업
클래스 생성
-패키지명 : com.wsy.boardsystem01.domain
-클래스 이름 : BoardVO
package com.wsy.boardsystem01.domain;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data //getter/setter, toString, 생성자 자동 완성
@NoArgsConstructor // 기본 생성자
@AllArgsConstructor //필드값을 전부 매개변수로 가지는 생성자, 디폴트 생성자 사라짐
public class BoardVO {
private int bno;
private String title;
}
└디폴트 생성자, getter/setter, toString()등이 생성되어 있는 것을 확인할 수 있음.
└필드값을 전부 매개변수로 가지는 생성자는 없음.
(2) 디비 연결 - 오라클과 MySQL
*오라클 패스워드 기억나지 않아 변경해야할 때,
-cmd 접속 >> sqlplus >> sys as sysdba >> (비번 입력하지 않고) 엔터 >> alter user system identified by 1234; (비번을 1234로 변경한다는 뜻) |
(2-1) 오라클
-시스템 계정 접속(오라클이 제공하는 슈퍼 관리자)
-워크시트 입력 내용
--계정생성
CREATE USER WSY IDENTIFIED BY 1234;
--접속 권한 설정 : 접속 권한, 리소스 접속 권한 세션&뷰 생성 권한
GRANT CONNECT, RESOURCE, CREATE SESSION, CREATE VIEW TO WSY;
--접속자 만들어야 함. 새로만들기
/*Name : wsy
사용자 이름 : (user 네임과 동일) WSY
비밀번호 : 1234
테스트 >> 접속*/
-시퀀스 및 테이블 생성
CREATE SEQUENCE SEQ_BOARD;
CREATE TABLE TBL_BOARD(
BNO NUMBER PRIMARY KEY,
TITLE VARCHAR2(200) NOT NULL,
CONTENT VARCHAR2(2000) NOT NULL,
WRITER VARCHAR2(50) NOT NULL,
REGDATE DATE DEFAULT SYSDATE,
UPDATEDATE DATE DEFAULT SYSDATE
);
SELECT * FROM TBL_BOARD;
>> STS4에서 필드 변수 수정_오라클에서 입력한 필드대로 추가해줄 것.
package com.wsy.boardsystem01.domain;
import java.util.Date;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data //getter/setter, toString, 생성자 자동 완성
@NoArgsConstructor // 기본 생성자
@AllArgsConstructor //필드값을 전부 매개변수로 가지는 생성자, 디폴트 생성자 사라짐
public class BoardVO {
private int bno;
private String title;
private String content;
private String writer;
private Date regdate;
private Date updatedate;
}
>> sql에서 데이터 입력
--여러 번 INSERT 10번 정도
INSERT INTO TBL_BOARD(BNO, TITLE, CONTENT, WRITER)
VALUES(SEQ_BOARD.NEXTVAL, '제목1', '테스트01', 'USER00');
SELECT * FROM TBL_BOARD;
>> 결과
>> 추가 설정
*오라클 최신버전은 maven repository에서 코드 복붙하면 되는데, 현재 버전에서는 jar 파일 다운받아서 사용
maven repository : ojdbc8 >> 21.5.0.0 >> jar 파일 다운 >> 원하는 장소에 옮기기
>> 현재 프로젝트 우클릭
>> Build Path >> Java Build Path >> Libraries : Add External JARs >> 다운받은 jar 선택
>> Deployment Assembly >> Add >> Archives from file system >> 다운받은 jar 선택
(*Build Path에서 이미 ojdbc8이 있다면, 경로만 수정해줘도 된다)
>> Apply and Close
>> 테스트
클래스 생성 : src/test/java (패키지 앤 폴더) - JDBCTest
package com.wsy.boardsystem01;
import java.sql.Connection;
import java.sql.DriverManager;
import org.junit.Test;
import lombok.extern.log4j.Log4j;
@Log4j
public class JDBCTest {
String url = "jdbc:oracle:thin:@localhost:1521:XE";
String username = "WSY";
String password = "1234";
//test하려면
@Test
public void testOracle() {
try {
Class.forName("oracle.jdbc.driver.OracleDriver");
Connection conn = DriverManager.getConnection(url, username, password);
log.info(conn); //콘솔에 출력
} catch (Exception e) {
e.printStackTrace();
}
}
}
>> JDBCTest.java [ V ] 화살표 모양 클릭 - 클릭 >> testOracle() 우클릭 >> Run As >> Junit Test
정상적으로 연결되었을 때, 아래와 같이 출력됨.
INFO : com.wsy.boardsystem01.JDBCTest - oracle.jdbc.driver.T4CConnection@6a4f1a55 |
└Junit : 단위 테스트.
※ 안 될 때, 오류 해결
*오라클 드라이브 not found면 드라이버를 못 찾는 것. 오타 등을 확인해볼 것.
*처음 연결 확인할 때, junit 때문이면 업데이트가 제대로 안 된 것.
*pom.xml에서 junit 버전 제대로 변경되었는지(중복 x), spring-test도 있어야 정상적으로 실행됨.
*STS4 종료 >> .m2 >> repository에서 삭제하면 업데이트한 내용 사라짐. 제대로 삭제 후 추가(설치)해야 할 때.
package com.wsy.boardsystem01;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import org.junit.Test;
import com.wsy.boardsystem01.domain.BoardVO;
import lombok.extern.log4j.Log4j;
@Log4j
public class JDBCTest {
String url = "jdbc:oracle:thin:@localhost:1521:XE";
String username = "WSY";
String password = "1234";
String sql = "SELECT * FROM TBL_BOARD";
//test하려면
@Test
public void testOracle() {
try {
Class.forName("oracle.jdbc.driver.OracleDriver");
Connection conn = DriverManager.getConnection(url, username, password);
//log.info(conn); //콘솔에 출력 //junit 확인 후 비활성화
PreparedStatement ps = conn.prepareStatement(sql);
ResultSet rs = ps.executeQuery(); // 테이블 내용 전부 가져와
while(rs.next()) {
BoardVO vo = new BoardVO();
vo.setBno(rs.getInt("bno"));
vo.setTitle(rs.getString("title"));
vo.setContent(rs.getString("content"));
vo.setWriter(rs.getString("writer"));
vo.setRegdate(rs.getDate("regdate"));
vo.setUpdatedate(rs.getDate("updatedate"));
log.info(vo);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
>> junit 테스트 하면, 콘솔에
(*만약에 출력 안 되면 sqldeveloper에서 COMMIT; 했는지 체크)
INFO : com.wsy.boardsystem01.JDBCTest - BoardVO(bno=1, title=제목1, content=테스트01, writer=USER00, regdate=2022-03-04, updatedate=2022-03-04) INFO : com.wsy.boardsystem01.JDBCTest - BoardVO(bno=2, title=제목1, content=테스트01, writer=USER00, regdate=2022-03-04, updatedate=2022-03-04) INFO : com.wsy.boardsystem01.JDBCTest - BoardVO(bno=3, title=제목1, content=테스트01, writer=USER00, regdate=2022-03-04, updatedate=2022-03-04) INFO : com.wsy.boardsystem01.JDBCTest - BoardVO(bno=4, title=제목1, content=테스트01, writer=USER00, regdate=2022-03-04, updatedate=2022-03-04) INFO : com.wsy.boardsystem01.JDBCTest - BoardVO(bno=5, title=제목1, content=테스트01, writer=USER00, regdate=2022-03-04, updatedate=2022-03-04) INFO : com.wsy.boardsystem01.JDBCTest - BoardVO(bno=6, title=제목1, content=테스트01, writer=USER00, regdate=2022-03-04, updatedate=2022-03-04) INFO : com.wsy.boardsystem01.JDBCTest - BoardVO(bno=7, title=제목1, content=테스트01, writer=USER00, regdate=2022-03-04, updatedate=2022-03-04) INFO : com.wsy.boardsystem01.JDBCTest - BoardVO(bno=8, title=제목1, content=테스트01, writer=USER00, regdate=2022-03-04, updatedate=2022-03-04) INFO : com.wsy.boardsystem01.JDBCTest - BoardVO(bno=9, title=제목1, content=테스트01, writer=USER00, regdate=2022-03-04, updatedate=2022-03-04) INFO : com.wsy.boardsystem01.JDBCTest - BoardVO(bno=10, title=제목1, content=테스트01, writer=USER00, regdate=2022-03-04, updatedate=2022-03-04) |
→ 위의 방법은 JSP하던 방법.
(2-2) MySQL
>> 뒤에서 설명
데이터베이스 커넥션 사용할 때,
한번 사용할 때마다 생성하고 사용 후 삭제하는데 귀찮음.
(3) 커넥션 풀
일정 개수의 커넥션을 만들어 넣어두면 필요한 사람이 연결해서 사용할 수 있음. 다중 사용자가 디비 사용할 때, 속도 빨라짐. 편이성. 효율성.
<!--pom.xml에 입력되어 있어야 함.-->
<!-- hikariCP :최신버전 -->
<!-- https://mvnrepository.com/artifact/com.zaxxer/HikariCP -->
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>5.0.1</version>
</dependency>
>> 폴더 : src / main / webapp / WEB-INF / spring : root-context.xml
(사용자 요청 처리 : servlet-context.xml
(데이터베이스 관련된 내용 : root-context.xml)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<!-- Root Context: defines shared resources visible to all other web components -->
<bean id="hikariConfig" class="com.zaxxer.hikari.HikariConfig">
<!-- 클래스에서 자동 완성해서 class 가져올 것 -->
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
<property name="jdbcUrl" value="jdbc:oracle:thin:@localhost:1521:XE"/>
<property name="username" value="WSY"/>
<property name="password" value="1234"/>
</bean>
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">
<constructor-arg ref="hikariConfig"/>
</bean>
<!-- 뒤에 쓸 건데 미리 입력해둔 것. -->
<context:component-scan base-package="com.wsy.boardsystem01"/>
</beans>
> JDBCTest.java에서
package com.wsy.boardsystem01;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.wsy.boardsystem01.domain.BoardVO;
import lombok.extern.log4j.Log4j;
@Log4j
//설정을 해줘야 testOracleDataSource를 junit할 수 있음.
@RunWith(SpringJUnit4ClassRunner.class) // 단위 테스트 하겠다.
//"" 안에서 컨트롤+스페이스 하면 자동 완성
@ContextConfiguration("file:src/main/webapp/WEB-INF/spring/root-context.xml") //여기에 있는 빈 중에서 dataSource를 쓰겠다.
public class JDBCTest {
//root-context.xml에서 설정완료 했다면 필요없음. 근데 testOracle 내용 오류나므로 일단 살려둠.
String url = "jdbc:oracle:thin:@localhost:1521:XE";
String username = "WSY";
String password = "1234";
String sql = "SELECT * FROM TBL_BOARD";
//★★★함수 만들어 테스트
@Autowired //만들어진 객체 autowired 주입
private DataSource dataSource;
@Test
public void testOracleDataSource() {
try {
Connection conn = dataSource.getConnection();
PreparedStatement ps = conn.prepareStatement(sql);
ResultSet rs = ps.executeQuery(); // 테이블 내용 전부 가져와
while(rs.next()) {
BoardVO vo = new BoardVO();
vo.setBno(rs.getInt("bno"));
vo.setTitle(rs.getString("title"));
vo.setContent(rs.getString("content"));
vo.setWriter(rs.getString("writer"));
vo.setRegdate(rs.getDate("regdate"));
vo.setUpdatedate(rs.getDate("updatedate"));
log.info(vo);
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//test하려면
//@Test
public void testOracle() {
try {
Class.forName("oracle.jdbc.driver.OracleDriver");
Connection conn = DriverManager.getConnection(url, username, password);
//log.info(conn); //콘솔에 출력 //junit 확인 후 비활성화
PreparedStatement ps = conn.prepareStatement(sql);
ResultSet rs = ps.executeQuery(); // 테이블 내용 전부 가져와
while(rs.next()) {
BoardVO vo = new BoardVO();
vo.setBno(rs.getInt("bno"));
vo.setTitle(rs.getString("title"));
vo.setContent(rs.getString("content"));
vo.setWriter(rs.getString("writer"));
vo.setRegdate(rs.getDate("regdate"));
vo.setUpdatedate(rs.getDate("updatedate"));
log.info(vo);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
>>root-context.xml 파일에서
namespaces >> context 체크 >> JDBCTest 수정
>> 결과 : 콘솔
INFO : org.springframework.test.context.support.DefaultTestContextBootstrapper - Loaded default TestExecutionListener class names from location [META-INF/spring.factories]: [org.springframework.test.context.web.ServletTestExecutionListener, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener, org.springframework.test.context.support.DependencyInjectionTestExecutionListener, org.springframework.test.context.support.DirtiesContextTestExecutionListener, org.springframework.test.context.transaction.TransactionalTestExecutionListener, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener, org.springframework.test.context.event.EventPublishingTestExecutionListener] INFO : org.springframework.test.context.support.DefaultTestContextBootstrapper - Using TestExecutionListeners: [org.springframework.test.context.web.ServletTestExecutionListener@4c39bec8, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener@f79e, org.springframework.test.context.support.DependencyInjectionTestExecutionListener@7ee8290b, org.springframework.test.context.support.DirtiesContextTestExecutionListener@1f59a598, org.springframework.test.context.transaction.TransactionalTestExecutionListener@1e178745, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener@192c3f1e, org.springframework.test.context.event.EventPublishingTestExecutionListener@26b3fd41] INFO : com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Starting... WARN : com.zaxxer.hikari.util.DriverDataSource - Registered driver with driverClassName=oracle.jdbc.driver.OracleDriver was not found, trying direct instantiation. INFO : com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Added connection oracle.jdbc.driver.T4CConnection@267f474e INFO : com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Start completed. INFO : com.wsy.boardsystem01.JDBCTest - BoardVO(bno=1, title=제목1, content=테스트01, writer=USER00, regdate=2022-03-04, updatedate=2022-03-04) INFO : com.wsy.boardsystem01.JDBCTest - BoardVO(bno=2, title=제목1, content=테스트01, writer=USER00, regdate=2022-03-04, updatedate=2022-03-04) INFO : com.wsy.boardsystem01.JDBCTest - BoardVO(bno=3, title=제목1, content=테스트01, writer=USER00, regdate=2022-03-04, updatedate=2022-03-04) INFO : com.wsy.boardsystem01.JDBCTest - BoardVO(bno=4, title=제목1, content=테스트01, writer=USER00, regdate=2022-03-04, updatedate=2022-03-04) INFO : com.wsy.boardsystem01.JDBCTest - BoardVO(bno=5, title=제목1, content=테스트01, writer=USER00, regdate=2022-03-04, updatedate=2022-03-04) INFO : com.wsy.boardsystem01.JDBCTest - BoardVO(bno=6, title=제목1, content=테스트01, writer=USER00, regdate=2022-03-04, updatedate=2022-03-04) INFO : com.wsy.boardsystem01.JDBCTest - BoardVO(bno=7, title=제목1, content=테스트01, writer=USER00, regdate=2022-03-04, updatedate=2022-03-04) INFO : com.wsy.boardsystem01.JDBCTest - BoardVO(bno=8, title=제목1, content=테스트01, writer=USER00, regdate=2022-03-04, updatedate=2022-03-04) INFO : com.wsy.boardsystem01.JDBCTest - BoardVO(bno=9, title=제목1, content=테스트01, writer=USER00, regdate=2022-03-04, updatedate=2022-03-04) INFO : com.wsy.boardsystem01.JDBCTest - BoardVO(bno=10, title=제목1, content=테스트01, writer=USER00, regdate=2022-03-04, updatedate=2022-03-04) INFO : com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown initiated... INFO : com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown completed. |
>> 위의 과정 귀찮음.
//JDBCTest.java
String sql = "SELECT * FROM TBL_BOARD";
//오라클, MySQL 테이블 이름 TBL_BOARD여야 함. 그래야 찾을 수 있음.
쿼리만 입력했을 때, 모든 작업이 되도록 하는 것 : MyBatis
*오라클은 DB를 많이 탐.
(2-2) MySQL
MySQL Workbench >> root : 비번 1234(설치 때 입력한 것)
-users and Privileges >> Add Account / Limit to Hosts Matching %(외부에서도 접근 가능) / pw 1234
>>Administrative role : DBA 체크
>> HOME :: my SQL connection : 계정으로 접속(이름 바꾸고 비밀번호 입력하고 테스트 후 접속)
*Schemas : 현재 데이터베이스 보여줌.
(*오라클은 계정에서 테이블만 만들면 됨. 보안적 측면에서 우위)
(*MySQL 외부에서 공유 가능)
*new schemas(드럼통 아이콘) >> name : springdb / charset : utf8 >> apply
*생성한 스키마 더블클릭 >> 테이블 우클릭 : 테이블 생성 : 이름 tbl_board(STS4의 쿼리문에서 테이블명이 이거기 때문)
-column name 입력. 필요한 거 체크. (*AI : Auto Increment : 오라클의 시퀀스와 비슷하나 차이점 존재.)
(오라클은 시퀀스가 독립적이라 번호 사용된....)
(+) 실제 생성한 스키마/테이블 이름 : springdb / board_tbl
└bno : INT / PK / NN / AI
└DATETIME(오라클의 날짜) - now() 입력해주면 입력 당시(?) 수정 당시의 시간(오라클의 SYSDATE)
**생성한 테이블에 커서 올리면 : 스패너 아이콘(설정 변경할 수 있음) / 테이블 아이콘(데이터 입력)
>> maven repository
(MySQL 버전 확인 >> programfile(x86) >> MySQL >> 버전 8
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.15</version>
</dependency>
위의 코드를 pom.xml에 붙여넣기
>> root-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<!-- Root Context: defines shared resources visible to all other web components -->
<!-- mySQL 메이븐 리파지토리에서 코드 복사 후 주석 처리 -->
<!-- ★★ 오라클 연결 -->
<!-- <bean id="hikariConfig" class="com.zaxxer.hikari.HikariConfig">
클래스에서 자동 완성해서 class 가져올 것
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
<property name="jdbcUrl" value="jdbc:oracle:thin:@localhost:1521:XE"/>
<property name="username" value="WSY"/>
<property name="password" value="1234"/>
</bean> -->
<!-- ★★ MySQL 연길 -->
<bean id="hikariConfig" class="com.zaxxer.hikari.HikariConfig">
<!-- 클래스에서 자동 완성해서 class 가져올 것 -->
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/> <!-- 옛날버전(현업에서 주로 쓰이는 5버전)일 땐, cj 빼면 된다. -->
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test01?useSSL=false&serverTimezone=Asia/Seoul&characterEncoding=UTF-8"/>
<property name="username" value="wsy"/>
<property name="password" value="1234"/>
</bean>
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">
<constructor-arg ref="hikariConfig"/>
</bean>
<!-- 뒤에 쓸 건데 미리 입력해둔 것. -->
<!-- <context:component-scan base-package="com.wsy.boardsystem01"/> -->
</beans>
**MySQL 설정 설명
<bean id="hikariConfig" class="com.zaxxer.hikari.HikariConfig">
<!-- 클래스에서 자동 완성해서 class 가져올 것 -->
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<!-- 옛날버전(현업에서 주로 쓰이는 5버전)일 땐, cj 빼면 된다. -->
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test01?useSSL=false&serverTimezone=Asia/Seoul&characterEncoding=UTF-8"/>
<!--test01은 MySQL 스키마 이름, 물음표(?) 이후의 내용은 MySQL 6버전부터 입력해줘야 함.
왜냐하면, 버전 6부터 한국 타임존과 문자 인코딩 설정이 빠졌기 때문에 직접 설정해줘야 함.-->
<property name="username" value="wsy"/>
<property name="password" value="1234"/>
</bean>
>> 결과
INFO : org.springframework.test.context.support.DefaultTestContextBootstrapper - Loaded default TestExecutionListener class names from location [META-INF/spring.factories]: [org.springframework.test.context.web.ServletTestExecutionListener, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener, org.springframework.test.context.support.DependencyInjectionTestExecutionListener, org.springframework.test.context.support.DirtiesContextTestExecutionListener, org.springframework.test.context.transaction.TransactionalTestExecutionListener, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener, org.springframework.test.context.event.EventPublishingTestExecutionListener] INFO : org.springframework.test.context.support.DefaultTestContextBootstrapper - Using TestExecutionListeners: [org.springframework.test.context.web.ServletTestExecutionListener@26b3fd41, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener@7494f96a, org.springframework.test.context.support.DependencyInjectionTestExecutionListener@561b6512, org.springframework.test.context.support.DirtiesContextTestExecutionListener@2e377400, org.springframework.test.context.transaction.TransactionalTestExecutionListener@1757cd72, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener@445b295b, org.springframework.test.context.event.EventPublishingTestExecutionListener@49e5f737] INFO : com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Starting... INFO : com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Added connection com.mysql.cj.jdbc.ConnectionImpl@64712be INFO : com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Start completed. INFO : com.wsy.boardsystem01.JDBCTest - BoardVO(bno=1, title=title01, content=content01, writer=wirter01, regdate=2022-03-04, updatedate=2022-03-04) INFO : com.wsy.boardsystem01.JDBCTest - BoardVO(bno=2, title=title02, content=content02, writer=writer02, regdate=2022-03-04, updatedate=2022-03-04) INFO : com.wsy.boardsystem01.JDBCTest - BoardVO(bno=3, title=title03, content=content03, writer=writer03, regdate=2022-03-04, updatedate=2022-03-04) INFO : com.wsy.boardsystem01.JDBCTest - BoardVO(bno=4, title=title04, content=content04, writer=writer04, regdate=2022-03-04, updatedate=2022-03-04) INFO : com.wsy.boardsystem01.JDBCTest - BoardVO(bno=5, title=title05, content=content05, writer=writer05, regdate=2022-03-04, updatedate=2022-03-04) INFO : com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown initiated... INFO : com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown completed. |
(4) MyBatis
쿼리만 입력했을 때, 모든 작업이 되도록 하는 것
public void testOracle() {
try {
Class.forName("oracle.jdbc.driver.OracleDriver");
Connection conn = DriverManager.getConnection(url, username, password);
//log.info(conn); //콘솔에 출력 //junit 확인 후 비활성화
PreparedStatement ps = conn.prepareStatement(sql);
ResultSet rs = ps.executeQuery(); // 테이블 내용 전부 가져와
while(rs.next()) {
BoardVO vo = new BoardVO();
vo.setBno(rs.getInt("bno"));
vo.setTitle(rs.getString("title"));
vo.setContent(rs.getString("content"));
vo.setWriter(rs.getString("writer"));
vo.setRegdate(rs.getDate("regdate"));
vo.setUpdatedate(rs.getDate("updatedate"));
log.info(vo);
}
} catch (Exception e) {
e.printStackTrace();
}
}
//데이터를 어레이리스트에 답는 과정은 생략되어 있음
>> 기존의 작업은 데이터를 받아와서 ArrayList에 데이터 담아서 리턴. 번거로움.
쿼리만 입력했을 때, 모든 작업이 되도록 하는 것.
root-context.xml
>> namespaces : myBatis-spirng 체크
root-context.xml 아랫부분에 코드 추가
<!-- myBatis 사용하기 위해서 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
</bean>
<mybatis-spring:scan base-package="com.wsy.boardsystem01.mapper"/>
<!-- 뒤에 쓸 건데 미리 입력해둔 것. -->
<context:component-scan base-package="com.wsy.boardsystem01"/>
>> 인터페이스 생성
패키지 : com.wsy.boardsystem01.mapper
이름 : BoardMapper
package com.wsy.boardsystem01.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import com.wsy.boardsystem01.domain.BoardVO;
@Mapper
public interface BoardMapper {
@Select("select * from tbl_board where bno>0")
public List<BoardVO> getList();
}
>> 클래스 생성
-패키지 : com.wsy.boardsystem01
-이름 : MapperTest
package com.wsy.boardsystem01;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.wsy.boardsystem01.domain.BoardVO;
import com.wsy.boardsystem01.mapper.BoardMapper;
import lombok.extern.log4j.Log4j;
//JDBCTest에서 가져오기
@Log4j
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("file:src/main/webapp/WEB-INF/spring/root-context.xml")
public class MapperTest {
@Autowired
private BoardMapper mapper;
@Test
public void testGetList() {
List<BoardVO> list = mapper.getList();
for(BoardVO vo:list) {
log.info(vo);
}
}
}
>> junit Run As
INFO : org.springframework.test.context.support.DefaultTestContextBootstrapper - Loaded default TestExecutionListener class names from location [META-INF/spring.factories]: [org.springframework.test.context.web.ServletTestExecutionListener, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener, org.springframework.test.context.support.DependencyInjectionTestExecutionListener, org.springframework.test.context.support.DirtiesContextTestExecutionListener, org.springframework.test.context.transaction.TransactionalTestExecutionListener, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener, org.springframework.test.context.event.EventPublishingTestExecutionListener] INFO : org.springframework.test.context.support.DefaultTestContextBootstrapper - Using TestExecutionListeners: [org.springframework.test.context.web.ServletTestExecutionListener@7d8704ef, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener@13b6aecc, org.springframework.test.context.support.DependencyInjectionTestExecutionListener@158a8276, org.springframework.test.context.support.DirtiesContextTestExecutionListener@3c3d9b6b, org.springframework.test.context.transaction.TransactionalTestExecutionListener@79d8407f, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener@5fbe4146, org.springframework.test.context.event.EventPublishingTestExecutionListener@1e66f1f5] INFO : com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Starting... INFO : com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Added connection com.mysql.cj.jdbc.ConnectionImpl@2f40a43 INFO : com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Start completed. INFO : com.wsy.boardsystem01.MapperTest - BoardVO(bno=1, title=title01, content=content01, writer=wirter01, regdate=Fri Mar 04 14:27:38 KST 2022, updatedate=Fri Mar 04 14:27:38 KST 2022) INFO : com.wsy.boardsystem01.MapperTest - BoardVO(bno=2, title=title02, content=content02, writer=writer02, regdate=Fri Mar 04 14:29:22 KST 2022, updatedate=Fri Mar 04 14:29:22 KST 2022) INFO : com.wsy.boardsystem01.MapperTest - BoardVO(bno=3, title=title03, content=content03, writer=writer03, regdate=Fri Mar 04 14:30:16 KST 2022, updatedate=Fri Mar 04 14:30:16 KST 2022) INFO : com.wsy.boardsystem01.MapperTest - BoardVO(bno=4, title=title04, content=content04, writer=writer04, regdate=Fri Mar 04 14:30:16 KST 2022, updatedate=Fri Mar 04 14:30:16 KST 2022) INFO : com.wsy.boardsystem01.MapperTest - BoardVO(bno=5, title=title05, content=content05, writer=writer05, regdate=Fri Mar 04 14:30:16 KST 2022, updatedate=Fri Mar 04 14:30:16 KST 2022) INFO : com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown initiated... INFO : com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown completed. |
// 방금 했던 작업이 연결 설정 완료 //
앞으로 계속 함수를 만들어 나갈 건데,
조건(where) 쓰고 하면 쿼리가 무거워질 것. → 쿼리 전문 xml 만들어서 사용
package com.wsy.boardsystem01.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import com.wsy.boardsystem01.domain.BoardVO;
@Mapper
public interface BoardMapper {
@Select("select * from tbl_board where bno>0") //이 쿼리문은 간단한 거
public List<BoardVO> getList();
//root-context 이동 >> sqlSessionFactory가 dataSource 만듦.
//스캔해서 mapper 자동 만듦.
//데이터 연결 정보 // 디비 접근
// 위 함수를 부르면, 위의 쿼리를 가지고 sqlSessionFactory 데이터 접근
//앞으로 계속 함수를 만들어 나가는데, 쿼리가 무거워질 것. 그래서 쿼리 전문 xml 만들어서 사용할 것.
public void insertBoard(BoardVO vo);
public BoardVO getBoard(int bno);
public void updateBoard(BoardVO vo);
public void deleteBoard(int bno);
}
...
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">
<constructor-arg ref="hikariConfig"/>
</bean>
...
<!-- myBatis 사용하기 위해서 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
</bean>
<!--MyBatis 쓰기 위해서 필요한 구문. sql 자체로 빈을 만들어서 작업하겠다.-->
<mybatis-spring:scan base-package="com.wsy.boardsystem01.mapper"/>
<!-- sqlSessionFactory은 여기(위 경로)에 있다. -->
<!-- 뒤에 쓸 건데 미리 입력해둔 것. -->
<context:component-scan base-package="com.wsy.boardsystem01"/>
...
>> 패키지 생성
-위치 : src/main/resources ← 패키지 우클릭해서 패키지 생성. ↓ 아래의 이름.
-이름 : (BoardMapper.java 경로) com.wsy.boardsystem01.mapper
>> 그 위치에 xml 파일 생성 : BoardMapper.xml(인터페이스 이름과 동일하게)
(크롬에서 구글링해서 필요한 부분만 취사선택
https://mybatis.org/mybatis-3/ko/index.html)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.wsy.boardsystem01.mapper.BoardMapper"> <!-- xml 파일 -->
<!-- BoardMapper.java에서 설정한 것 -->
<select id="getList" resultType="com.wsy.boardsystem01.domain.BoardVO">
select * from tbl_board where bno > 0 <!-- where을 사용하면 index를 사용한다. -->
<!-- BaordMapper.java에서 쿼리문 비활성화 -->
</select>
</mapper>
-BoardMapper.java
package com.wsy.boardsystem01.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import com.wsy.boardsystem01.domain.BoardVO; //BoardMapper.xml에서 resultType에 넣을 것
@Mapper
public interface BoardMapper {
// @Select("select * from tbl_board where bno>0") //★xml 설정에 따라 쿼리문 주석처리.
public List<BoardVO> getList(); //BoardMapper.xml에서 select id
//root-context 이동 >> sqlSessionFactory가 dataSource 만듦.
//스캔해서 mapper 자동 만듦.
//데이터 연결 정보 // 디비 접근
// 위 함수를 부르면, 위의 쿼리를 가지고 sqlSessionFactory 데이터 접근
//위의 쿼리문은 간단한 거라서...위처럼 작성
//앞으로 계속 함수를 만들어 나가는데, 쿼리가 무거워질 것. 그래서 쿼리 전문 xml 만들어서 사용할 것.
public void insertBoard(BoardVO vo);
public BoardVO getBoard(int bno);
public void updateBoard(BoardVO vo);
public void deleteBoard(int bno);
}
>> Test(junit Run As)
INFO : org.springframework.test.context.support.DefaultTestContextBootstrapper - Loaded default TestExecutionListener class names from location [META-INF/spring.factories]: [org.springframework.test.context.web.ServletTestExecutionListener, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener, org.springframework.test.context.support.DependencyInjectionTestExecutionListener, org.springframework.test.context.support.DirtiesContextTestExecutionListener, org.springframework.test.context.transaction.TransactionalTestExecutionListener, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener, org.springframework.test.context.event.EventPublishingTestExecutionListener] INFO : org.springframework.test.context.support.DefaultTestContextBootstrapper - Using TestExecutionListeners: [org.springframework.test.context.web.ServletTestExecutionListener@7d8704ef, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener@13b6aecc, org.springframework.test.context.support.DependencyInjectionTestExecutionListener@158a8276, org.springframework.test.context.support.DirtiesContextTestExecutionListener@3c3d9b6b, org.springframework.test.context.transaction.TransactionalTestExecutionListener@79d8407f, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener@5fbe4146, org.springframework.test.context.event.EventPublishingTestExecutionListener@1e66f1f5] INFO : com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Starting... INFO : com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Added connection com.mysql.cj.jdbc.ConnectionImpl@22db8f4 INFO : com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Start completed. INFO : com.wsy.boardsystem01.MapperTest - BoardVO(bno=1, title=title01, content=content01, writer=wirter01, regdate=Fri Mar 04 14:27:38 KST 2022, updatedate=Fri Mar 04 14:27:38 KST 2022) INFO : com.wsy.boardsystem01.MapperTest - BoardVO(bno=2, title=title02, content=content02, writer=writer02, regdate=Fri Mar 04 14:29:22 KST 2022, updatedate=Fri Mar 04 14:29:22 KST 2022) INFO : com.wsy.boardsystem01.MapperTest - BoardVO(bno=3, title=title03, content=content03, writer=writer03, regdate=Fri Mar 04 14:30:16 KST 2022, updatedate=Fri Mar 04 14:30:16 KST 2022) INFO : com.wsy.boardsystem01.MapperTest - BoardVO(bno=4, title=title04, content=content04, writer=writer04, regdate=Fri Mar 04 14:30:16 KST 2022, updatedate=Fri Mar 04 14:30:16 KST 2022) INFO : com.wsy.boardsystem01.MapperTest - BoardVO(bno=5, title=title05, content=content05, writer=writer05, regdate=Fri Mar 04 14:30:16 KST 2022, updatedate=Fri Mar 04 14:30:16 KST 2022) INFO : com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown initiated... INFO : com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown completed. |
(5) 게시물 등록
(5-1) BoardMapper.java
함수 수정
package com.wsy.boardsystem01.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import com.wsy.boardsystem01.domain.BoardVO; //BoardMapper.xml에서 resultType에 넣을 것
@Mapper
public interface BoardMapper {
// @Select("select * from tbl_board where bno>0") //★xml 설정에 따라 쿼리문 주석처리.
public List<BoardVO> getList(); //BoardMapper.xml에서 select id
//root-context 이동 >> sqlSessionFactory가 dataSource 만듦.
//스캔해서 mapper 자동 만듦.
//데이터 연결 정보 // 디비 접근
// 위 함수를 부르면, 위의 쿼리를 가지고 sqlSessionFactory 데이터 접근
//위의 쿼리문은 간단한 거라서...위처럼 작성
//앞으로 계속 함수를 만들어 나가는데, 쿼리가 무거워질 것. 그래서 쿼리 전문 xml 만들어서 사용할 것.
//게시물 등록
public void insert(BoardVO vo);
public BoardVO getBoard(int bno);
public void update(BoardVO vo);
public void delete(int bno);
}
(5-2) BoardMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.wsy.boardsystem01.mapper.BoardMapper"> <!-- xml 파일 -->
<select id="getList" resultType="com.wsy.boardsystem01.domain.BoardVO"> <!-- BoardMapper.java에서 설정한 것 -->
<!-- -->
select * from tbl_board where bno > 0 <!-- where을 사용하면 index를 사용한다. -->
<!-- BaordMapper.java에서 쿼리문 비활성화 -->
</select>
<insert id="insert">
insert into tbl_board(title, content, writer)
values(#{title}, #{content}, #{writer} )<!-- 나머진 자동 증가, 자동 입력되니까. -->
</insert>
</mapper>
(5-3) MapperTest.java
package com.wsy.boardsystem01;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.wsy.boardsystem01.domain.BoardVO;
import com.wsy.boardsystem01.mapper.BoardMapper;
import lombok.extern.log4j.Log4j;
//JDBCTest에서 가져오기
@Log4j
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("file:src/main/webapp/WEB-INF/spring/root-context.xml")
public class MapperTest {
@Autowired
private BoardMapper mapper;
@Test
public void testGetList() {
List<BoardVO> list = mapper.getList();
for(BoardVO vo:list) {
log.info(vo);
}
}
@Test
public void testInsert() {
//setter 사용
BoardVO vo = new BoardVO();
vo.setTitle("제목001++++++");
vo.setContent("내용001++++++");
vo.setWriter("작성자1++++++");
mapper.insert(vo);
}
}
>> MapperTest junit : testInsert() 확인 → testGetList() 확인
INFO : org.springframework.test.context.support.DefaultTestContextBootstrapper - Loaded default TestExecutionListener class names from location [META-INF/spring.factories]: [org.springframework.test.context.web.ServletTestExecutionListener, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener, org.springframework.test.context.support.DependencyInjectionTestExecutionListener, org.springframework.test.context.support.DirtiesContextTestExecutionListener, org.springframework.test.context.transaction.TransactionalTestExecutionListener, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener, org.springframework.test.context.event.EventPublishingTestExecutionListener] INFO : org.springframework.test.context.support.DefaultTestContextBootstrapper - Using TestExecutionListeners: [org.springframework.test.context.web.ServletTestExecutionListener@1190200a, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener@6a2f6f80, org.springframework.test.context.support.DependencyInjectionTestExecutionListener@45b4c3a9, org.springframework.test.context.support.DirtiesContextTestExecutionListener@399c4be1, org.springframework.test.context.transaction.TransactionalTestExecutionListener@291caca8, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener@385e9564, org.springframework.test.context.event.EventPublishingTestExecutionListener@5b94b04d] INFO : com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Starting... INFO : com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Added connection com.mysql.cj.jdbc.ConnectionImpl@3e2fc448 INFO : com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Start completed. INFO : com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown initiated... INFO : com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown completed. |
INFO : org.springframework.test.context.support.DefaultTestContextBootstrapper - Loaded default TestExecutionListener class names from location [META-INF/spring.factories]: [org.springframework.test.context.web.ServletTestExecutionListener, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener, org.springframework.test.context.support.DependencyInjectionTestExecutionListener, org.springframework.test.context.support.DirtiesContextTestExecutionListener, org.springframework.test.context.transaction.TransactionalTestExecutionListener, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener, org.springframework.test.context.event.EventPublishingTestExecutionListener] INFO : org.springframework.test.context.support.DefaultTestContextBootstrapper - Using TestExecutionListeners: [org.springframework.test.context.web.ServletTestExecutionListener@1190200a, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener@6a2f6f80, org.springframework.test.context.support.DependencyInjectionTestExecutionListener@45b4c3a9, org.springframework.test.context.support.DirtiesContextTestExecutionListener@399c4be1, org.springframework.test.context.transaction.TransactionalTestExecutionListener@291caca8, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener@385e9564, org.springframework.test.context.event.EventPublishingTestExecutionListener@5b94b04d] INFO : com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Starting... INFO : com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Added connection com.mysql.cj.jdbc.ConnectionImpl@3e2fc448 INFO : com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Start completed. INFO : com.wsy.boardsystem01.MapperTest - BoardVO(bno=1, title=title01, content=content01, writer=wirter01, regdate=Fri Mar 04 14:27:38 KST 2022, updatedate=Fri Mar 04 14:27:38 KST 2022) INFO : com.wsy.boardsystem01.MapperTest - BoardVO(bno=2, title=title02, content=content02, writer=writer02, regdate=Fri Mar 04 14:29:22 KST 2022, updatedate=Fri Mar 04 14:29:22 KST 2022) INFO : com.wsy.boardsystem01.MapperTest - BoardVO(bno=3, title=title03, content=content03, writer=writer03, regdate=Fri Mar 04 14:30:16 KST 2022, updatedate=Fri Mar 04 14:30:16 KST 2022) INFO : com.wsy.boardsystem01.MapperTest - BoardVO(bno=4, title=title04, content=content04, writer=writer04, regdate=Fri Mar 04 14:30:16 KST 2022, updatedate=Fri Mar 04 14:30:16 KST 2022) INFO : com.wsy.boardsystem01.MapperTest - BoardVO(bno=5, title=title05, content=content05, writer=writer05, regdate=Fri Mar 04 14:30:16 KST 2022, updatedate=Fri Mar 04 14:30:16 KST 2022) INFO : com.wsy.boardsystem01.MapperTest - BoardVO(bno=6, title=제목001++++++, content=내용001++++++, writer=작성자1++++++, regdate=Fri Mar 04 16:24:10 KST 2022, updatedate=Fri Mar 04 16:24:10 KST 2022) INFO : com.wsy.boardsystem01.MapperTest - BoardVO(bno=7, title=제목001++++++, content=내용001++++++, writer=작성자1++++++, regdate=Fri Mar 04 16:24:31 KST 2022, updatedate=Fri Mar 04 16:24:31 KST 2022) INFO : com.wsy.boardsystem01.MapperTest - BoardVO(bno=8, title=제목001++++++, content=내용001++++++, writer=작성자1++++++, regdate=Fri Mar 04 16:26:56 KST 2022, updatedate=Fri Mar 04 16:26:56 KST 2022) INFO : com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown initiated... INFO : com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown completed. |
└test할 때마다 추가되는듯
>> insert 추가 생성
-BoardMapper.java
package com.wsy.boardsystem01.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import com.wsy.boardsystem01.domain.BoardVO; //BoardMapper.xml에서 resultType에 넣을 것
@Mapper
public interface BoardMapper {
// @Select("select * from tbl_board where bno>0") //★xml 설정에 따라 쿼리문 주석처리.
public List<BoardVO> getList(); //BoardMapper.xml에서 select id
//root-context 이동 >> sqlSessionFactory가 dataSource 만듦.
//스캔해서 mapper 자동 만듦.
//데이터 연결 정보 // 디비 접근
// 위 함수를 부르면, 위의 쿼리를 가지고 sqlSessionFactory 데이터 접근
//위의 쿼리문은 간단한 거라서...위처럼 작성
//앞으로 계속 함수를 만들어 나가는데, 쿼리가 무거워질 것. 그래서 쿼리 전문 xml 만들어서 사용할 것.
//게시물 등록
public void insert(BoardVO vo);
public BoardVO read(int bno); //bno를 넘겨줌.
public void update(BoardVO vo);
public void delete(int bno);
//나중에
// public void insertSelectKey(BoardVO vo); // 게시물 저장할 때, 앞의 것보다 증가해서 게시물번호 설정. 근데 게시물번호를 땡겨서 써야할 때
//↑ 현재 저장할 게시물 번호를 땡겨서 써야할 때? 알아봐야 할때 사용하는 것
}
-BoardMapper.xml
패키지폴더 : src/mai/resouces/com/wsy/boardsystem01/mapper/Boardmapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.wsy.boardsystem01.mapper.BoardMapper"> <!-- xml 파일 -->
<select id="getList" resultType="com.wsy.boardsystem01.domain.BoardVO"> <!-- BoardMapper.java에서 설정한 것 -->
<!-- -->
select * from tbl_board where bno > 0 <!-- where을 사용하면 index를 사용한다. -->
<!-- BaordMapper.java에서 쿼리문 비활성화 -->
</select>
<insert id="insert">
insert into tbl_board(title, content, writer)
values(#{title}, #{content}, #{writer} )<!-- 나머진 자동 증가, 자동 입력되니까. -->
<!-- get함수에서 꺼내오는 것 -->
</insert>
<!-- 오라클에서 사용하는 방법 -->
<!-- <insert id="insertSelectKey">
<selectKey keyProperty="bno" order="BEFORE" resultType="int" />
insert하기 전에 미리 만들어라. 게시물 번호를 미리 선점한다는 뜻?
select
</insert> -->
<select id="read"> <!-- 매퍼의 함수 이름들 -->
select * from tbl_board where bno=#{bno}
</select>
<update id="update">
update tbl_board set title=#{title}, content=#{content}, writer=#{writer}, updatedate=#{update}
where bno=#{bno}
</update>
<delete id="delete">
delete from tbl_board where bno=#{bno}
</delete>
</mapper>
controller / service / view
*있는 코드로 설정 변경해보기*
2차 게시판 : 조회수, 댓글, 검색 기능 붙이기
- 2022.03.07 -
※ 임포트(Import)할 때, 수정해야 할 부분 ※
*오라클 드라이버 연결 설정 : 프로젝트 우클릭>> Build Path >> 오라클 드라이버(ojdbc8-21.5.0.0)>> delpoyment and Assembly >> ojdbc8 추가
*root-context.xml : 오라클 또는 MySQL이냐에 따라 활성화시켜야 할 것이 다름.
★프로젝트 실행(Run As) 했을 때, 404 에러 뜨는 것
home 페이지를 제대로 연결하지 않았기 때문(?)
-pom.xml의 javax.servlet-api 3.1.0 주석처리, 2.5 버전은 활성화되어 있어야 함.
// 본인 프로젝트가 아닐 경우 버전 상충 때문
>> 메이븐 업데이터 >> 프로젝트 실행 : 홈화면이 제대로 떠야 함.
테스트 위치, 루트 컨텍스트에서 데이터소스(dataSource)를 만들기 때문에
데이터 생성하는 것이 상충해서 그런 것.
패키지 생성 : com.wsy.boardsystem01.test 우클릭 >> com.wsy.boardsystem01.test
만들어서 JDBCTest.java와 MapperTest.java 파일 옮기기
-root-context.xml
오라클인지, MySQL인지 확인 / MySQL에 스키마 이름이 있는지 확인 / 아이디 비번 확인
*JRE 라이브러리 버전 1.6인 걸 변경하는 것
프로젝트 >> 빌드 패스 >>JRE System Library >> version 11 변경
>> BoardMapper.xml
결과를 resultType(BoardVO)에 실음. 이때 데이터가 여러 개이면 자동으로 ArrayList로 넣음
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.wsy.boardsystem01.mapper.BoardMapper"> <!-- xml 파일 -->
<select id="getList" resultType="com.wsy.boardsystem01.domain.BoardVO"> <!-- BoardMapper.java에서 설정한 것 -->
<!-- 결과를 resultType에 실음 -->
<!-- 여러 개일 때, 자동으로 ArrayList로 넣음 -->
select * from tbl_board where bno > 0 <!-- where을 사용하면 index를 사용한다. -->
<!-- BaordMapper.java에서 쿼리문 비활성화 -->
</select>
<insert id="insert">
insert into tbl_board(title, content, writer)
values(#{title}, #{content}, #{writer} )<!-- 나머진 자동 증가, 자동 입력되니까. -->
<!-- get함수에서 꺼내오는 것 -->
</insert>
<!-- 오라클에서 사용하는 방법 -->
<!-- <insert id="insertSelectKey">
<selectKey keyProperty="bno" order="BEFORE" resultType="int" />
insert하기 전에 미리 만들어라. 게시물 번호를 미리 선점한다는 뜻?
select
</insert> -->
<select id="read"> <!-- 매퍼의 함수 이름들 -->
select * from tbl_board where bno=#{bno}
</select>
<update id="update">
update tbl_board set title=#{title}, content=#{content}, writer=#{writer}, updatedate=#{update}
where bno=#{bno}
</update>
<delete id="delete">
delete from tbl_board where bno=#{bno}
</delete>
</mapper>
>> BoardMapper.java
getList를 호출하면 리스트형식으로 받아옴(?)
package com.wsy.boardsystem01.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import com.wsy.boardsystem01.domain.BoardVO; //BoardMapper.xml에서 resultType에 넣을 것
@Mapper
public interface BoardMapper {
// @Select("select * from tbl_board where bno>0") //★xml 설정에 따라 쿼리문 주석처리.
public List<BoardVO> getList(); //BoardMapper.xml에서 select id
//겟리스트를 호출하면 리스트형식으로 받아옴.
//root-context 이동 >> sqlSessionFactory가 dataSource 만듦.
//스캔해서 mapper 자동 만듦.
//데이터 연결 정보 // 디비 접근
// 위 함수를 부르면, 위의 쿼리를 가지고 sqlSessionFactory 데이터 접근
//위의 쿼리문은 간단한 거라서...위처럼 작성
//앞으로 계속 함수를 만들어 나가는데, 쿼리가 무거워질 것. 그래서 쿼리 전문 xml 만들어서 사용할 것.
//게시물 등록
public void insert(BoardVO vo);
public BoardVO read(int bno); //bno를 넘겨줌.
public void update(BoardVO vo);
public void delete(int bno);
//나중에
// public void insertSelectKey(BoardVO vo); // 게시물 저장할 때, 앞의 것보다 증가해서 게시물번호 설정. 근데 게시물번호를 땡겨서 써야할 때
//↑ 현재 저장할 게시물 번호를 땡겨서 써야할 때? 알아봐야 할때 사용하는 것
}
받아온 걸 보여주는 게 서비스(service)?
앞으로 내용이 변경되지 않도록 하는 게 중요! → 인터페이스 생성
(7) Service
(7-1) 패키지 생성 및 클래스 생성
-패키지 : com.wsy.boardsystem01.service
-클래스 : BoardServiceImple.java
package com.wsy.boardsystem01.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.wsy.boardsystem01.domain.BoardVO;
import com.wsy.boardsystem01.mapper.BoardMapper;
@Service
public class BoardServiceImple implements BoardService {
//root-context.xml에 component자동 생성하라고 설정해두었음.
// <context:component-scan base-package="com.wsy.boardsystem01"/>
@Autowired
BoardMapper boardMapper;
@Override
public List<BoardVO> getList() {
return boardMapper.getList();
}
}
(7-2) 인터페이스 생성
-클래스 : com.wsy.boardsystem01.service
-인터페이스 : BoardService
package com.wsy.boardsystem01.service;
import java.util.List;
import com.wsy.boardsystem01.domain.BoardVO;
public interface BoardService {
List<BoardVO> getList();
}
(7-3) 컨트롤러 생성
-패키지 : com.wsy.boardsystem01.controller
-클래스 이름 : BoardController
package com.wsy.boardsystem01.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import com.wsy.boardsystem01.domain.BoardVO;
import com.wsy.boardsystem01.service.BoardService;
@Controller //얘때문에 객체 생성
public class BoardController {
//서비스 만드는 법
@Autowired //디폴트 생성자를 통해서 세팅
BoardService boardService; // 서비스는 BoardServiceImple에서 생성됨
//콘트롤러 클래스 역할을 하는 함수
@RequestMapping("board/list") //board 아래의 list 연결
public String list(Model model) {
List<BoardVO> list = boardService.getList();
model.addAttribute("list", list);
return "board/list"; //리턴 타입이 String일 때 사용. servlet-context.xml의 viewresolve에서 prefix, suffix 설정한 것
}
}
(7-4)
-폴더 생성 : src/mai/webapp/WEB-INF / views / board
-JSP : list.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<table>
<tr>
<td>번호</td>
<td>제목</td>
<td>작성자</td>
<td>등록일</td>
<td>수정일</td>
</tr>
<!-- items은 model.addAttribute("list", list);의 쌍따옴표의 이름 -->
<c:forEach items="${list }" var="board"> <!-- 가져온 것(list)를 board에 넣음 -->
<tr>
<td>${board.bno }</td>
<!--boardController의 read mapping 이름이 같아야 함. detail -->
<td><a href="/board/detail?bno=${board.bno }">${board.title }</a></td>
<%-- <td><a href="/board/read/get?bno=${board.bno }"/>${board.title }</td> --%>
<td>${board.writer }</td>
<td>${board.regdate }</td>
<td>${board.updatedate }</td>
</tr>
</c:forEach>
</table>
</body>
</html>
└데이터가 잘 오는지 확인
(8) 데이터 하나 불러오기
상세보기
>>기존 내용에서 수정 및 추가 : boardMapper.xml / BoardService 인터페이스 / BaordServiceImpl / BoardController /
public BoardVO read(int bno); //bno를 넘겨줌.
package com.wsy.boardsystem01.service;
import java.util.List;
import com.wsy.boardsystem01.domain.BoardVO;
public interface BoardService {
List<BoardVO> getList();
BoardVO read(int bno);
}
package com.wsy.boardsystem01.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.wsy.boardsystem01.domain.BoardVO;
import com.wsy.boardsystem01.mapper.BoardMapper;
@Service //얘 때문에 객체 생성
public class BoardServiceImple implements BoardService {
//root-context.xml에 component자동 생성하라고 설정해두었음.
// <context:component-scan base-package="com.wsy.boardsystem01"/>
@Autowired
BoardMapper boardMapper;
@Override
public List<BoardVO> getList() {
return boardMapper.getList();
}
@Override
public BoardVO read(int bno) {
return boardMapper.read(bno);
}
}
package com.wsy.boardsystem01.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import com.wsy.boardsystem01.domain.BoardVO;
import com.wsy.boardsystem01.service.BoardService;
@Controller //얘때문에 객체 생성
@RequestMapping("/board/**") //공통부분 간소화하기 위해
public class BoardController {
//서비스 만드는 법
@Autowired //디폴트 생성자를 통해서 세팅
BoardService boardService; // 서비스는 BoardServiceImple에서 생성됨
//콘트롤러 클래스 역할을 하는 함수
@GetMapping("list") //board 아래의 list 연결
public String list(Model model) {
List<BoardVO> list = boardService.getList();
model.addAttribute("list", list);
return "board/list"; //리턴 타입이 String일 때 사용. servlet-context.xml의 viewresolve에서 prefix, suffix 설정한 것
}
//내가 하던 것
// @GetMapping("board/content")
// public String read(Model model) {
// BoardVO read = boardService.read("bno");
// model.addAttribute("list", read);
// return "board/content";
// }
@GetMapping("detail")
public String read(int bno, Model model) { //원래 매개변수, 데이터 보낼 매개변수
model.addAttribute("board", boardService.read(bno));
return "board/detail";
}
//detail 하는 방법 2 - @requestParam은 꼭 입력해줘야 오류가 안 남.
@GetMapping("detail")
public String read(@RequestParam("bno") int bno, Model model) { //원래 매개변수, 데이터 보낼 매개변수
model.addAttribute("board", boardService.read(bno));
return "board/detail";
}
}
getMapping의 이름이랑 jsp 파일 이름 같아야 찾을 수 있음.
>> detail.jsp 생성
리스트의 태그립 복붙
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h3>Content Page</h3>
<a href="/board/list">list</a>
<table>
<tr>
<td>번호</td> <td>${board.bno }</td>
</tr>
<tr>
<td>제목</td> <td>${board.title }</td>
</tr>
<tr>
<td>작성자</td> <td>${board.writer }</td>
</tr>
<tr>
<td>내용</td> <td>${board.content }</td>
</tr>
<tr>
<td>등록일</td> <td>${board.regdate }</td>
</tr>
<tr>
<td>수정일</td> <td>${board.updatedate }</td>
</tr>
</table>
</body>
</html>
>> 프로젝트 실행했을 때, 리스트 페이지(/board/list)에서 상세보기(/board/detail)로 넘어가지 않는 오류. 500 내부서버 오류. resultType이 없다고? 하는 오류
>> BoardMapper.xml에서 resultType 설정을 해줘야 함.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.wsy.boardsystem01.mapper.BoardMapper"> <!-- xml 파일 -->
<select id="getList" resultType="com.wsy.boardsystem01.domain.BoardVO"> <!-- BoardMapper.java에서 설정한 것 -->
<!-- 결과를 resultType에 실음 -->
<!-- 여러 개일 때, 자동으로 ArrayList로 넣음 -->
select * from tbl_board where bno > 0 <!-- where을 사용하면 index를 사용한다. -->
<!-- BaordMapper.java에서 쿼리문 비활성화 -->
</select>
<!-- 데이터를 하나만 가져올 때, -->
<insert id="insert">
insert into tbl_board(title, content, writer)
values(#{title}, #{content}, #{writer} )<!-- 나머진 자동 증가, 자동 입력되니까. -->
<!-- get함수에서 꺼내오는 것 -->
</insert>
<!-- 오라클에서 사용하는 방법 -->
<!-- <insert id="insertSelectKey">
<selectKey keyProperty="bno" order="BEFORE" resultType="int" />
insert하기 전에 미리 만들어라. 게시물 번호를 미리 선점한다는 뜻?
select
</insert> -->
<!--resulType 입력-->
<select id="read" resultType="com.wsy.boardsystem01.domain.BoardVO"> <!-- 매퍼의 함수 이름들 -->
select * from tbl_board where bno=#{bno}
</select>
<update id="update">
update tbl_board set title=#{title}, content=#{content}, writer=#{writer}, updatedate=#{update}
where bno=#{bno}
</update>
<delete id="delete">
delete from tbl_board where bno=#{bno}
</delete>
</mapper>
└(JSP에서) resultSet하던 것은 resultType 넣어줘야 함. 조회하던 것을 객체 타입으로 리턴하기 때문에.
그러므로 update나 delete는 resultType 할 필요없음.
BaordMapper.xml
select 결과 resultSet일 때, resultType을 넣어줘야 함.
End
『부트스트랩』
하나의 형식(List)은 만들어져있음. 거기에 일단 부트스트랩으로 디자인 넣음. 서식만 바꾼 것.
(제공되는 자료 파일에서 부트스트랩 적용)
부트스트랩 CDN 제공하는 곳 : https://maxcdn.bootstrapcdn.com
(1) List.jsp 부트스트랩 적용
(1-1) 공통 영역인 header와 footer 생성
>> src / main / webapp / WEB-INF / views에 includes 폴더 생성
-각각 header.jsp / footer.jsp 생성
<!-header.jsp--->
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<div class="jumbotron" style="margin-bottom: 0">
<h1>MyBoard</h1>
</div>
<nav class="navbar navbar-expand-sm bg-dark navbar-dark mb-3">
<!-- Brand/logo -->
<a class="navbar-brand" href="#">HOME</a>
<!-- Links -->
<ul class="navbar-nav mr-auto">
<li class="nav-item"><a class="nav-link" href="#">Board</a>
</li>
<li class="nav-item"><a class="nav-link" href="#"> FILE</a></li>
<li class="nav-item"><a class="nav-link" href="#"> FILEBOARD</a></li>
</ul>
<ul class="navbar-nav">
<li class="nav-item"><a class="nav-link" href="#">로그인</a></li>
<li class="nav-item"><a class="nav-link" href="#">회원가입</a></li>
</ul>
</nav>
</body>
</html>
<!-footer.jsp--->
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<footer class="page-footer font-small blue"> <!-- ★★공통영역 footer -->
<div class="footer-copyright text-center py-3">
2022 Copyright;
<a href='#'>busan.com</a>
</div>
</footer>
</body>
</html>
(1-2) list.jsp 수정
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ include file="../includes/header.jsp"%> <!--공통영역 header include-->
<!--부트스트랩 가져오기-->
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
<!--참고자료 list.html에서 본문 내용 가져와 수정-->
<div class="container">
<h2>BoardList(10)</h2>
<div class="form-group text-right">
<button type="button" class="btn btn-secondary btn-sm" id="btnWrite">글쓰기</button>
</div>
<table class="table table-hover">
<thead>
<tr>
<th>번호</th>
<th>작성자</th>
<th>제목</th>
<th>작성일시</th>
<th>조회수</th>
</tr>
</thead>
<tbody>
<!--forEach로 자동 생성-->
<c:forEach items="${list}" var="board">
<tr>
<td>${board.bno}</td>
<td><a href="/board/detail?bno=${board.bno }">${board.title }</a></td>
<td> ${board.writer}</td>
<td><fmt:formatDate pattern="yyyy-MM-dd" value="${board.regdate}"/></td>
<td> ${board.updatedate}</td>
</tr>
</c:forEach>
</tbody>
</table>
<div class="d-flex justify-content-between mt-3">
<ul class="pagination">
<!-- 이전 -->
<li class="page-item"><a class="page-link" href="#">Previous</a></li>
<!--페이지 리스트-->
<li class="page-item"><a class="page-link" href="#">1</a></li>
<li class="page-item"><a class="page-link" href="#">2</a></li>
<li class="page-item"><a class="page-link" href="#">3</a></li>
<li class="page-item"><a class="page-link" href="#">4</a></li>
<li class="page-item"><a class="page-link" href="#">5</a></li>
<li class="page-item"><a class="page-link" href="#">6</a></li>
<li class="page-item"><a class="page-link" href="#">7</a></li>
<li class="page-item"><a class="page-link" href="#">8</a></li>
<li class="page-item"><a class="page-link" href="#">9</a></li>
<li class="page-item"><a class="page-link" href="#">10</a></li>
<!-- 다음 -->
<li class="page-item"><a class="page-link" href="#">Next</a></li>
</ul>
<form class="form-inline" action="#" id="searchFrm">
<select name="field" class="form-control mb-2 mr-sm-2">
<option value="writer">작성자</option>
<option value="title">제목</option>
</select> <input type="text" class="form-control mb-2 mr-sm-2"
placeholder="Enter Search" name="word">
<button type="submit" class="btn btn-secondary mb-2 btn-sm">Search</button>
</form>
</div>
</div>
<%@ include file="../includes/footer.jsp"%><!--공통영역 footer include-->
(2) content(detail) 부트스트랩 적용
>>jsp 파일 생성 : src / main / webapp / WEB-INF / views에 detail.jsp 생성
-참고자료의 content.html에서 container 영역 붙이기
-공통영역인 header.jsp, footer.jsp 인클루드
-일부 내용 수정 ${board.}
(*아래의 name이 제대로 수정되지 않았음)
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ include file="../includes/header.jsp"%> <!-- header include -->
<!-- bootstarp 서식 가져오기 -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
<!-- content.html container ~ footer 직전의 </div>까지 -->
<div class="container">
<h2>${board.writer } 글보기</h2><!-- ★★ -->
<div class="form-group">
<label for="num">글번호</label>
<input type="text" class="form-control" id="num" name="num" value="${board.bno}" readonly="readonly">
</div>
<div class="form-group">
<label for="title">제목</label>
<input type="text" class="form-control" id="title" name="title" value="${board.title}" readonly="readonly">
</div>
<div class="form-group">
<label for="writer">등록일</label>
<input type="text" class="form-control" id="writer"name="writer" value="${board.regdate}" readonly="readonly">
</div>
<div class="form-group">
<label for="writer">수정일</label>
<input type="text" class="form-control" id="writer"name="writer" value="${board.updatedate}" readonly="readonly">
</div>
<div class="form-group">
<label for="writer">조회수</label>
<input type="text" class="form-control" id="writer"name="writer" value="" readonly="readonly">
</div>
<div class="form-group">
<label for="content">내용</label>
<textarea class="form-control" rows="5" id="content" name="content" readonly="readonly">${board.content}</textarea>
</div>
<div class="form-group text-right">
<button type="button" class="btn btn-secondary btn-sm" id="btnUpdate">수정하기</button>
<button type="button" class="btn btn-secondary btn-sm" id="btnDelete">삭제하기</button>
</div>
<div class="container mt-5">
<div class="form-group">
<label for="comment">Comment:</label>
<textarea class="form-control" rows="5" id="msg" name="text"></textarea>
</div>
<button type="button" class="btn btn-success" id="commentBtn">Comment Write</button>
</div>
<div id="replyResult">댓글 리스트 영역 </div>
</div>
<%@ include file="../includes/footer.jsp"%> <!-- footer include -->
>> 현재 진행상태
-리스트 페이지, 상세보기(detail.jsp)의 부트스트랩 서식 적용
-리스트 페이지 : 디비의 자료 가져와 출력
-페이지 연결 : 리스트 페이지에서 글 제목 클릭하면 해당하는 글 페이지로 이동. 상세보기(detail.jsp)
(9) 글 등록
(9-1) jsp 생성
-board / register.jsp
(9-2) BoardController.java 수정
package com.wsy.boardsystem01.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import com.wsy.boardsystem01.domain.BoardVO;
import com.wsy.boardsystem01.service.BoardService;
@Controller //얘때문에 객체 생성
@RequestMapping("/board/**") //공통부분 간소화하기 위해
public class BoardController {
//서비스 만드는 법
@Autowired //디폴트 생성자를 통해서 세팅
BoardService boardService; // 서비스는 BoardServiceImple에서 생성됨
//콘트롤러 클래스 역할을 하는 함수
@GetMapping("list") //board 아래의 list 연결
public String list(Model model) {
List<BoardVO> list = boardService.getList();
model.addAttribute("list", list);
return "board/list"; //리턴 타입이 String일 때 사용. servlet-context.xml의 viewresolve에서 prefix, suffix 설정한 것
}
// @GetMapping("board/content")
// public String read(Model model) {
// BoardVO read = boardService.read("bno");
// model.addAttribute("list", read);
// return "board/content";
// }
@GetMapping("detail")
public String read(int bno, Model model) { //원래 매개변수, 데이터 보낼 매개변수
model.addAttribute("board", boardService.read(bno));
return "board/detail";
}
//이것만 추가하면 됨.
@GetMapping("register")
public void register() {
}
}
(9-3)
리스트 페이지에서 글쓰기 했을 때, 글쓰기 페이지로 이동 : list.jsp에서 수정
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<%@ include file="../includes/header.jsp"%>
<div class="container">
<!-- ★★공통영역 header ↑-->
<h2>BoardList(10)</h2>
<div class="form-group text-right">
<button type="button" class="btn btn-secondary btn-sm" id="btnWrite">글쓰기</button>
</div>
<table class="table table-hover">
<thead>
<tr>
<th>번호</th>
<th>작성자</th>
<th>제목</th>
<th>작성일시</th>
<th>조회수</th>
</tr>
</thead>
<tbody>
<c:forEach items="${list}" var="board">
<tr>
<td>${board.bno}</td>
<td><a href="/board/detail?bno=${board.bno }">${board.title }</a></td>
<td>${board.writer}</td>
<td><fmt:formatDate pattern="yyyy-MM-dd"
value="${board.regdate}" /></td>
<td>${board.updatedate}</td>
</tr>
</c:forEach>
</tbody>
</table>
<div class="d-flex justify-content-between mt-3">
<ul class="pagination">
<!-- 이전 -->
<li class="page-item"><a class="page-link" href="#">Previous</a></li>
<!--페이지 리스트-->
<li class="page-item"><a class="page-link" href="#">1</a></li>
<li class="page-item"><a class="page-link" href="#">2</a></li>
<li class="page-item"><a class="page-link" href="#">3</a></li>
<li class="page-item"><a class="page-link" href="#">4</a></li>
<li class="page-item"><a class="page-link" href="#">5</a></li>
<li class="page-item"><a class="page-link" href="#">6</a></li>
<li class="page-item"><a class="page-link" href="#">7</a></li>
<li class="page-item"><a class="page-link" href="#">8</a></li>
<li class="page-item"><a class="page-link" href="#">9</a></li>
<li class="page-item"><a class="page-link" href="#">10</a></li>
<!-- 다음 -->
<li class="page-item"><a class="page-link" href="#">Next</a></li>
</ul>
<form class="form-inline" action="#" id="searchFrm">
<select name="field" class="form-control mb-2 mr-sm-2">
<option value="writer">작성자</option>
<option value="title">제목</option>
</select> <input type="text" class="form-control mb-2 mr-sm-2"
placeholder="Enter Search" name="word">
<button type="submit" class="btn btn-secondary mb-2 btn-sm">Search</button>
</form>
</div>
</div>
<!-- Script -->
<script type="text/javascript">
//btnWrite를 클릭하면 아래의 함수를 실행하라
$("#btnWrite").click(function(){
location.href="/board/register"
});
//태그 선택자
</script>
<%@ include file="../includes/footer.jsp"%>
>> header.jsp에서 링크 수정
어떤 페이지든 [Board]를 눌렀을 때, 리스트 페이지로 이동하게 하려면
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
<title>Insert title here</title>
</head>
<body>
<div class="jumbotron" style="margin-bottom: 0">
<h1>MyBoard</h1>
</div>
<nav class="navbar navbar-expand-sm bg-dark navbar-dark mb-3">
<!-- Brand/logo -->
<!-- ★★★ -->
<a class="navbar-brand" href="/index">HOME</a>
<!-- Links -->
<ul class="navbar-nav mr-auto">
<!-- ★★★ -->
<li class="nav-item"><a class="nav-link" href="/board/list">Board</a>
</li>
<li class="nav-item"><a class="nav-link" href="#"> FILE</a></li>
<li class="nav-item"><a class="nav-link" href="#"> FILEBOARD</a></li>
</ul>
<ul class="navbar-nav">
<li class="nav-item"><a class="nav-link" href="#">로그인</a></li>
<li class="nav-item"><a class="nav-link" href="#">회원가입</a></li>
</ul>
</nav>
</body>
</html>
(10) 글쓰기(insert) register.jsp
한바퀴 돌릴 수 있는지 확인
BoardMapper.xml
-bno : 자동 증가
-등록일/수정일 : 자동 수정
<insert id="insert">
insert into tbl_board(title, content, writer)
values(#{title}, #{content}, #{writer} )<!-- 나머진 자동 증가, 자동 입력되니까. -->
<!-- get함수에서 꺼내오는 것 -->
</insert>
interface BaordMapper
public void insert(BoardVO vo);
service : interface BoardService.java
package com.wsy.boardsystem01.service;
import java.util.List;
import com.wsy.boardsystem01.domain.BoardVO;
public interface BoardService {
List<BoardVO> getList();
BoardVO read(int bno);
void insert(BoardVO vo); //추가 입력
}
BoardServiceImple.java
package com.wsy.boardsystem01.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.wsy.boardsystem01.domain.BoardVO;
import com.wsy.boardsystem01.mapper.BoardMapper;
@Service //얘 때문에 객체 생성
public class BoardServiceImple implements BoardService {
//root-context.xml에 component자동 생성하라고 설정해두었음.
// <context:component-scan base-package="com.wsy.boardsystem01"/>
@Autowired
BoardMapper boardMapper;
//getList - list 목록 가져오기
@Override
public List<BoardVO> getList() {
return boardMapper.getList();
}
//read
@Override
public BoardVO read(int bno) {
return boardMapper.read(bno);
}
//insert
@Override
public void insert(BoardVO vo) {
boardMapper.insert(vo);
}
}
BoardController.java
절대경로로 적어줬음. → 상대경로. 맨 위에서 공통부분을 적어줬기 때문에.
@RequestMapping("/board/**") //공통부분 간소화하기 위해
...
@PostMapping("insert")
public String register(BoardVO vo) {
boardService.insert(vo);
//다른 거 호출할 때,
//return "redirect:/board/list";
return "redirect:list"; //requestMapping에서 board를 가지고 오고 있기 때문에 이렇게만 적어도 됨.
}
>> register.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ include file="../includes/header.jsp"%>
<!-- controller -->
<div class="container">
<h2>글쓰기</h2>
<form action="insert" method="post"> <!--postMapping 써야 함.-->
<div class="form-group">
<label for="title">제목:</label>
<input type="text" class="form-control" id="title" placeholder="Enter title" name="title">
</div>
<div class="form-group">
<label for="writer">작성자:</label>
<input type="text" class="form-control" id="writer" name="writer">
</div>
<div class="form-group">
<label for="content">내용:</label>
<textarea class="form-control" rows="5" id="content" name="content"></textarea>
</div>
<button type="submit" class="btn btn-primary btn-sm">Submit</button>
</form>
</div>
<%@ include file="../includes/footer.jsp"%>
(11) index 생성
-src / main / webapp / WEB-INF / views : index.jsp
샘플자료에서 container 영역만 복사 붙여넣기, header와 footer include하기
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ include file="includes/header.jsp"%> <!-- 경로 -->
<div class="container">
<!-- Carousel -->
<div id="carouselExampleControls" class="carousel slide container mt-4" data-ride="carousel">
<div class="carousel-inner">
<div class="carousel-item active">
<img src="resources/images/image1.jpg" width="400" height="500" class="d-block w-100">
</div>
<div class="carousel-item">
<img src="resources/images/image2.jpg" width="400" height="500" class="d-block w-100">
</div>
<div class="carousel-item">
<img src="resources/images/image3.jpg" width="400" height="500" class="d-block w-100">
</div>
<div class="carousel-item">
<img src="resources/images/image4.jpg"width="400" height="500" class="d-block w-100">
</div>
</div>
<a class="carousel-control-prev" href="#carouselExampleControls" role="button" data-slide="prev">
<span class="carousel-control-prev-icon" aria-hidden="true"></span>
<span class="sr-only">Previous</span>
</a>
<a class="carousel-control-next" href="#carouselExampleControls" role="button" data-slide="next">
<span class="carousel-control-next-icon" aria-hidden="true"></span>
<span class="sr-only">Next</span>
</a>
</div>
<!-- end of Carousel -->
<div class="container text-center mt-5">
<div class="row">
<div class="col-lg-4">
<img src="resources/images/image1.jpg" width="140" height="140" class="rounded-circle">
<h2>Heading</h2>
<p>Some representative placeholder content for the three columns of text below the carousel. This is the first column.</p>
<p><a class="btn btn-secondary" href="#">View details »</a></p>
</div><!-- /.col-lg-4 -->
<div class="col-lg-4">
<img src="resources/images/image2.jpg" width="140" height="140" class="rounded-circle">
<h2>Heading</h2>
<p>Another exciting bit of representative placeholder content. This time, we've moved on to the second column.</p>
<p><a class="btn btn-secondary" href="#">View details »</a></p>
</div><!-- /.col-lg-4 -->
<div class="col-lg-4">
<img src="resources/images/image3.jpg" width="140" height="140" class="rounded-circle">
<h2>Heading</h2>
<p>And lastly this, the third column of representative placeholder content.</p>
<p><a class="btn btn-secondary" href="#">View details »</a></p>
</div><!-- /.col-lg-4 -->
</div><!-- /.row -->
</div>
<div class="container mt-5">
<!-- Card Deck -->
<div class="card-deck">
<div class="card">
<img src="resources/images/image1.jpg" class="card-img-top">
<div class="card-body">
<h5 class="card-title">Card title</h5>
<p class="card-text">This is a longer card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
<p class="card-text"><small class="text-muted">Last updated 3 mins ago</small></p>
</div>
</div>
<div class="card">
<img src="resources/images/image2.jpg" class="card-img-top">
<div class="card-body">
<h5 class="card-title">Card title</h5>
<p class="card-text">This card has supporting text below as a natural lead-in to additional content.</p>
<p class="card-text"><small class="text-muted">Last updated 3 mins ago</small></p>
</div>
</div>
<div class="card">
<img src="resources/images/image3.jpg" class="card-img-top">
<div class="card-body">
<h5 class="card-title">Card title</h5>
<p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This card has even longer content than the first to show that equal height action.</p>
<p class="card-text"><small class="text-muted">Last updated 3 mins ago</small></p>
</div>
</div>
</div>
<!-- end of Card Deck -->
</div>
<!-- Jumbotron -->
<div class="jumbotron container mt-5">
<h1 class="image-4">Hello, world!</h1>
<p class="lead">This is a simple hero unit, a simple jumbotron-style component for calling extra attention to featured content or information.</p>
<hr class="my-4">
<p>It uses utility classes for typography and spacing to space content out within the larger container.</p>
<a class="btn btn-primary btn-lg" href="#" role="button">Learn more</a>
</div>
<!-- end of Jumbotron -->
</div>
<%@ include file="includes/footer.jsp"%>
-HomeController.java
package com.wsy.boardsystem01;
import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
/**
* Handles requests for the application home page.
*/
@Controller
public class HomeController {
private static final Logger logger = LoggerFactory.getLogger(HomeController.class);
/**
* Simply selects the home view to render by returning its name.
*/
@RequestMapping({"/", "/index"}) //root 또는 index일 때, index
public String home(Locale locale, Model model) {
logger.info("Welcome home! The client locale is {}.", locale);
return "index";
}
}
└프로젝트 실행했을 때, index.jsp가 실행되도록 (그리고 루트(/)일 때도 인덱스가 되도록 설정.)
(12) 이미지 출력
-폴더 및 그림 파일 넣기
src / main / webapp / resources >> images 폴더 생성
샘플 폴더에서 그림 사진 복붙하기 → 그러고 재실행하면 이미지 정상적 출력
정적인 파일을 쓸 수 있도록 하는 설정은,
정적은 파일은 webapp >> resources 폴더 안에 폴더 생성해서 넣음
>>servlet-context.xml
(src / main / webapp / WEB-INF / spring / appServlet)
<resources mapping="/resources/**" location="/resources/" />
리소스를 reousrces 폴더 안에 넣을 거고 경로는 resources/**(urlMapping)으로 넣겠다.
ex) index.jsp
<!--index.jsp image 삽입한 방법-->
<img src="resources/images/image1.jpg" width="400" height="500" class="d-block w-100">
<!--절대경로 사용법_경로가 애매할 때, 이렇게 사용-->
<img src="/resources/images/image1.jpg" width="400" height="500" class="d-block w-100">
└절대 경로를 쓰면, 파일의 위치가 변경되도 상관없지만 상대경로일 경우는데 파일의 위치가 바뀌면 로드가 안 됨.
(+) 리스트.jsp에서
<c:forEach items="${list}" var="board">
<tr>
<td>${board.bno}</td>
<!--1번-->
<%-- <td><a href="/board/detail?bno=${board.bno }">${board.title }</a></td> --%>
<!-- 경로에 번호를 바로 적어줄 때, BoardController 수정해야함.-->
<td><a href="/board/detail/${board.bno }">${board.title }</a></td> <!--2-->
<td>${board.writer}</td>
<td><fmt:formatDate pattern="yyyy-MM-dd"
value="${board.regdate}" /></td>
<td>${board.updatedate}</td>
</tr>
</c:forEach>
-BoardController.java
@GetMapping("detail/{bno}")
public String read(@PathVariable("bno") int bno, Model model) {
model.addAttribute("board", boardService.read(bno));
return "board/detail";
}
(+) 레코드 개수
<div class="container">
<!-- ★★공통영역 header ↑-->
<!--<h2>BoardList(10)--></h2> <!-- 레코드 개수 -->
<h2>BoardList(${boardCount })</h2>
<div class="form-group text-right">
<button type="button" class="btn btn-secondary btn-sm" id="btnWrite">글쓰기</button>
</div>
-BoardMapper.xml
<!-- record Count -->
<select id="boardCount" resultType="int">
select count(*) from tbl_board <!-- 테이블의 개수 리턴 -->
</select>
-interface BoardMapper.java
//레코드 개수
public int boardCount();
**서비스**
-interface BoardService.java
int boardCount();
-BoardServiceImple
@Override
public int boardCount() {
return boardMapper.boardCount();
}
-BoardController.java
@GetMapping("list") //board 아래의 list 연결
public String list(Model model) {
List<BoardVO> list = boardService.getList();
model.addAttribute("list", list);
model.addAttribute("boardCount", boardService.boardCount()); //★ 추가
return "board/list"; //리턴 타입이 String일 때 사용. servlet-context.xml의 viewresolve에서 prefix, suffix 설정한 것
}
(13) 글 업데이트 및 삭제
>>BoardMapper.java 리턴형 수정
public int update(BoardVO vo);
public int delete(int bno);
>>BoardMapper.xml
<update id="update">
update tbl_board set title=#{title}, content=#{content}, writer=#{writer}, updatedate=now()
where bno=#{bno}
</update>
<delete id="delete">
delete from tbl_board where bno=#{bno}
</delete>
>> BoardService .java - 인터페이스
package com.wsy.boardsystem01.service;
import java.util.List;
import com.wsy.boardsystem01.domain.BoardVO;
public interface BoardService {
List<BoardVO> getList();
BoardVO read(int bno);
void insert(BoardVO vo);
int boardCount();
boolean update(BoardVO vo);
boolean delete(int bno);
}
>>BoardController.java
//update
@GetMapping("update/{bno}")
public String update(@PathVariable("bno") int bno, Model model) {
model.addAttribute("board", boardService.read(bno));
return "board/update";
}
@PostMapping("update")
public String update(BoardVO vo) {
//메시지 띄울 때 사용하는 방법
// if(boardService.update(vo)) {
// return "redirect:/board/list";
// }else {
//
// }
boardService.update(vo);
return "redirect:/board/list";
}
//delete
@GetMapping("delete/{bno}")
public String delete(@PathVariable("bno") int bno) {
boardService.delete(bno);
return "redirect:/board/list";
}
>> detail.jsp(상세보기)
수정/삭제 버튼 눌렀을 때, 수정/삭제되도록
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ include file="../includes/header.jsp"%> <!-- header include -->
<!-- content.html container ~ footer 직전의 </div>까지 -->
<div class="container">
<h2>${board.writer } 글보기</h2><!-- ★★ -->
<div class="form-group">
<label for="num">글번호</label>
<input type="text" class="form-control" id="num" name="num" value="${board.bno}" readonly="readonly">
</div>
<div class="form-group">
<label for="title">제목</label>
<input type="text" class="form-control" id="title" name="title" value="${board.title}">
</div>
<div class="form-group">
<label for="writer">등록일</label>
<input type="text" class="form-control" id="writer"name="writer" value="${board.regdate}" readonly="readonly">
</div>
<div class="form-group">
<label for="writer">수정일</label>
<input type="text" class="form-control" id="writer"name="writer" value="${board.updatedate}" readonly="readonly">
</div>
<div class="form-group">
<label for="writer">조회수</label>
<input type="text" class="form-control" id="writer"name="writer" value="" readonly="readonly">
</div>
<div class="form-group">
<label for="content">내용</label>
<textarea class="form-control" rows="5" id="content" name="content">${board.content}</textarea>
</div>
<div class="form-group text-right">
<button type="button" class="btn btn-secondary btn-sm" id="btnUpdate">수정하기</button>
<button type="button" class="btn btn-secondary btn-sm" id="btnDelete">삭제하기</button>
</div>
<div class="container mt-5">
<div class="form-group">
<label for="comment">Comment:</label>
<textarea class="form-control" rows="5" id="msg" name="text"></textarea>
</div>
<button type="button" class="btn btn-success" id="commentBtn">Comment Write</button>
</div>
<div id="replyResult">댓글 리스트 영역 </div>
</div>
<!-- getMapping 호출 -->
<script type="text/javascript">
$("#btnUpdate").click(function(){
if(comfirm("정말 수정할까요?")){
location.href="/board/update/${board.bno}"
}
});
$("#btnDelete").click(function(){
if(comfirm("정말 삭제할까요??")){
location.href="/board/delete/${board.bno}"
}
});
</script>
<%@ include file="../includes/footer.jsp"%> <!-- footer include -->
>>jsp 파일 생성 : update.jsp만 생성/수정하면 됨.
(register.jsp 복사해서 이름 변경)
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ include file="../includes/header.jsp"%>
<!-- controller -->
<div class="container">
<h2>글쓰기</h2>
<!-- ★ -->
<form action="/board/update" method="post"><!-- boardcontroller의 post 방식 update 호출 -->
<div class="form-group">
<label for="title">번호:</label> <!-- 수정불가 -->
<input type="text" class="form-control" id="bno" name="bno" value="${board.bno }" readonly>
</div>
<div class="form-group">
<label for="title">제목:</label>
<input type="text" class="form-control" id="title" value="${board.title }" name="title">
</div>
<div class="form-group">
<label for="writer">작성자:</label>
<input type="text" class="form-control" id="writer" name="writer" value="${board.writer }">
</div>
<div class="form-group">
<label for="content">내용:</label>
<textarea class="form-control" rows="5" id="content" name="content">${board.content }</textarea>
</div>
<button type="submit" class="btn btn-primary btn-sm">Submit</button>
<!-- -->
</form>
</div>
<%@ include file="../includes/footer.jsp"%>
(웹 게시판 CRUD) 기본 사이클 완성
>> 클론 코딩으로
여기까지 만들어보기 / 1교시까지
- 2022.03.07 수업 코드 -
* * * *
- 2022.03.04 수업 코드 -
End2