수업/└Spring Framework

[04]웹 게시판(CRUD)_1차

onlyun 2022. 3. 4. 09:13

-순서 정리 : 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(드라이버 로딩), junitspring-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

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&amp;serverTimezone=Asia/Seoul&amp;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&amp;serverTimezone=Asia/Seoul&amp;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 &raquo;</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 &raquo;</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 &raquo;</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.07.zip
2.86MB

 

*   *   *   *

 

- 2022.03.04 수업 코드 -

 

2022.03.04.zip
0.04MB

End2