20220512 복습

2022 - 0512 - 복습


package com.jslhrd.sample;

import org.springframework.stereotype.Component;

import lombok.Data;

@Component //스프링에게 해당 클래스가 스프링에서 관리해야 하는 대상임을 표시
@Data //Lombok(setter,생성자,toString()등)을 자동 생성하는 어노테이션
public class Chef {
     
}


package com.jslhrd.sample;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import lombok.Data;
import lombok.Setter;

@Component //스프링에게 해당 클래스가 스프링에서 관리해야 하는 대상임을 표시
@Data //Lombok(setter,생성자,toString()등)을 자동 생성하는 어노테이션
public class Restaurant {

     @Setter(onMethod_ = @Autowired) // 의존성
     private Chef chef;
}


package com.jslhrd.sample;

import static org.junit.Assert.assertNotNull;

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 lombok.Setter;
import lombok.extern.log4j.Log4j;

@RunWith(SpringJUnit4ClassRunner.class) //현재 테스트 코드가 스프링을 실행하는 역할을 할 것이다.
@ContextConfiguration("file:src/main/webapp/WEB-INF/spring/root-context.xml") //필요한 객체들을 스프링 내에 객체로 등록(Spring's Bean)
@Log4j //Lombok을 이용하여 로그기록하는 Logger를 변수로 생성.
public class SampleTests {

     @Setter(onMethod_ = @Autowired) //@Autowired = 해당 인스턴스 변수가 스프링으로부터 자동으로 주입해 달라는 표시.
     private Restaurant restaurant;

     @Test
     public void testExist() {

          assertNotNull(restaurant); //restaurant변수가 null이 아니어야만 테스트가 성공한다는 뜻.

          log.info(restaurant);
          log.info("------------------");
          log.info(restaurant.getChef());
     }
}


package com.jslhrd.persistence;

import static org.junit.Assert.fail;
import java.sql.Connection;
import java.sql.DriverManager;

import org.junit.Test;

import lombok.extern.log4j.Log4j;

@Log4j
public class JDBCTests {

     static {
          try {
               Class.forName("oracle.jdbc.driver.OracleDriver");
          } catch (Exception e) {
               e.printStackTrace();
          }
     }

     @Test
     public void testConnection() {
          try {
               Connection con = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:XE","system","123456");
               log.info(con);
               log.info("성공");
               Class.forName("oracle.jdbc.driver.OracleDriver");
          } catch (Exception e) {
               log.info("실패");
               e.printStackTrace(); //
               fail(e.getMessage());
          }
     }

}


import static org.junit.Assert.fail;

import java.sql.Connection;

import javax.sql.DataSource;

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 lombok.Setter;
import lombok.extern.log4j.Log4j;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("file:src/main/webapp/WEB-INF/spring/root-context.xml")
@Log4j
public class DataSourceTests {

     @Setter(onMethod_ = @Autowired)
     private DataSource ds;

     @Test
     public void testConnection(){

          try(Connection con = ds.getConnection()) {

               log.info(con);
               log.info("success!!!");

          } catch (Exception e) {
               e.printStackTrace();
               fail(e.getMessage());
          }
     }
}


package com.jslhrd.domain;

import java.sql.Date;

import lombok.Data;

@Data
//Lombok(setter,생성자,toString()등)을 자동 생성하는 어노테이션
public class BoardVO {

     private long bno;
     private String title;
     private String content;
     private String writer;
     private Long hit;
     private Date regdate;
     private Date updatedate;
}


package com.jslhrd.mapper;
import java.util.List;
import com.jslhrd.domain.BoardVO;

public interface BoardMapper {

     public List<BoardVO> getList();

     public void insert(BoardVO board);

     public void insertSelectKey(BoardVO board);

     public BoardVO read(Long bno);

     public int update(BoardVO board);

     public int delete(Long bno);

}


package com.jslhrd.mapper;

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.jslhrd.domain.BoardVO;

import lombok.Setter;
import lombok.extern.log4j.Log4j;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("file:src/main/webapp/WEB-INF/spring/root-context.xml")
@Log4j
public class BoardMapperTests {

     @Setter(onMethod_ = @Autowired)
     private BoardMapper mapper;

     @Test
     public void testGetList() {
          mapper.getList().forEach(board -> log.info(board));
     }

     @Test
     public void testInsert() {
          BoardVO board = new BoardVO();
           board.setTitle("insert");
          board.setContent("insert");
           board.setWriter("insert");
          mapper.insert(board);
           log.info("-------------------insert result-----------------------");
          log.info(board);
          log.info("------------------------------------------");
     }

     @Test
     public void testInsertSelectKey() {
          BoardVO board = new BoardVO();
          board.setTitle("insert K");
          board.setContent("insert K");
          board.setWriter("insert K");
          mapper.insertSelectKey(board);
          log.info("-------------------insert Select Key result-----------------------");
          log.info(board);
          log.info("------------------------------------------");
     }

     @Test
     public void testRead() {
          BoardVO board = mapper.read(16L);
          log.info("-------------------read result-----------------------");
          log.info(board);
          log.info("------------------------------------------");
     }

     @Test
     public void testUpdate() {
          BoardVO board = new BoardVO();
          board.setBno(16L);
          board.setTitle("update");
          board.setContent("update");
          board.setWriter("update");
          log.info("UPDATE RESULT !!! : " + mapper.update(board));

     }
     @Test
     public void testDelete() {
          int count = mapper.delete(31L);
          log.info("DELETE RESULT !!! : "+count);
     }


}


package com.jslhrd.service;
import java.util.List;
import com.jslhrd.domain.BoardVO;
public interface BoardService {

     public List<BoardVO> getList();

     public BoardVO get(Long bno);

     public void insert(BoardVO board);

     public boolean update(BoardVO board);

     public boolean delete(Long bno);

}


package com.jslhrd.service;
import java.util.List;
import org.springframework.stereotype.Service;
import com.jslhrd.domain.BoardVO;
import com.jslhrd.mapper.BoardMapper;
import lombok.AllArgsConstructor;
import lombok.extern.log4j.Log4j;

@Service // 계층 구조상 주로 비즈니스 영역을 담당하는 객체임을 표시하기 위해 사용함.
@Log4j
@AllArgsConstructor // 생성자를 만들고 자동으로 주입함. (생성자를 만들지 않을 경우에는 @setter()를 이용해서 처리함)
public class BoardServiceImpl implements BoardService{

     // @Setter(onMethod_ = @Autowired)
     private BoardMapper mapper;

     @Override
     public List<BoardVO> getList(){
          log.info("GET LIST ----------------------------");
          return mapper.getList();
     }

     @Override
     public BoardVO get(Long bno) {
          log.info("GET : " + bno);
          return mapper.read(bno);
     }

     @Override
     public void insert(BoardVO board) {
          log.info("INSERT : " + board);
          mapper.insertSelectKey(board);
     }

     @Override
     public boolean update(BoardVO board) {
          log.info("UPDATE : " + board);
          return mapper.update(board) == 1;
     }

     @Override
     public boolean delete(Long bno) {
          log.info("DELETE : " + bno);
          return mapper.delete(bno) == 1;
     }

}


package com.jslhrd.service;

import static org.junit.Assert.assertNotNull;
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.jslhrd.domain.BoardVO;
import lombok.Setter;
import lombok.extern.log4j.Log4j;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("file:src/main/webapp/WEB-INF/spring/root-context.xml")
@Log4j
public class BoardServiceTests {

     @Setter(onMethod_ = @Autowired)
     private BoardService service;

     @Test
     public void testExist() {
          log.info(service);
          assertNotNull(service);
     }

     @Test
     public void testGetList() {
          service.getList().forEach(board -> log.info(board));
     }

     @Test
     public void testGet() {
          log.info("GET ---------------------------------------");
          log.info(service.get(17L));
     }
     @Test
     public void testInsert() {
          log.info("INSERT --------------------------------------");
          BoardVO board = new BoardVO();
          board.setTitle("insert");
          board.setContent("insert");
          board.setWriter("insert");
          service.insert(board);
     }

     @Test
     public void testUpdate() {
          BoardVO board = service.get(17L);
          if(board == null) {
               return;
          }
          board.setTitle("update");
          board.setContent("update");
          board.setWriter("update");
          log.info("UPDATE RESULT : " + service.update(board));
     }

     @Test
     public void testDelete() {
          log.info("DELETE RESULT : " + service.delete(30L));
     }

}


package com.jslhrd.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import com.jslhrd.domain.BoardVO;
import com.jslhrd.service.BoardService;

import lombok.AllArgsConstructor;
import lombok.extern.log4j.Log4j;

@Controller //스프링의 빈으로 인식할 수 있게 함.
@Log4j
@RequestMapping("/board/*") //'/board'로 시작하는 모든 처리를 BoardController가 하도록 지정.
@AllArgsConstructor
public class BoardController {

     private BoardService service;

     @GetMapping("/t1")
     public void t1() {

     }

     @GetMapping("/t2")
     public void t2() {

     }

     @GetMapping("/list")
     public void list(Model model) {
          log.info("list --------------------------------------------------");
          model.addAttribute("list",service.getList());
     }

     @GetMapping("/insert")
     public void insert() {

     }

     @PostMapping("/insert") //새롭게 등록된 게시물의 번호를 같이 전달(RedirectAttributes)
     public String insert(BoardVO board, RedirectAttributes rttr) {
          log.info("INSERT : " + board);
          service.insert(board);
          rttr.addFlashAttribute("result", board.getBno());
          return "redirect:/board/list";
     }

     @GetMapping({"/get","/update"}) // get방식의 get(),update()는 bno값을 받아 BoardVO를 전달하는 동일한 처리방식
     public void get(@RequestParam("bno") Long bno, Model model) { //bno를 좀 더 명시적으로 처리(@RequestParam), 화면쪽으로 게시물 전달(Model)
          log.info("get --------------------------------------------------");
          model.addAttribute("board",service.get(bno));
     }

     @PostMapping("/update")
     public String update(BoardVO board, RedirectAttributes rttr) {
          log.info("update --------------------------------------------------");
          if(service.update(board)) {
               rttr.addFlashAttribute("result", "update_success");
          }
          return "redirect:/board/list";
     }

     @PostMapping("/delete")
     public String delete(@RequestParam("bno") Long bno, RedirectAttributes rttr) {
          log.info("delete --------------------------------------------------");
          if(service.delete(bno)) {
               rttr.addFlashAttribute("result","delete_success");
          }
          return "redirect:/board/list";
     }


}


package com.jslhrd.controller;

import org.junit.Before;
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 org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

import lombok.Setter;
import lombok.extern.log4j.Log4j;

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration //Servlet의 ServletContext 이용. 스프링에서는 WebApplicationContext라는 존재를 이용.
@ContextConfiguration({"file:src/main/webapp/WEB-INF/spring/root-context.xml",
"file:src/main/webapp/WEB-INF/spring/appServlet/servlet-context.xml"})
@Log4j
public class BoardControllerTests {

     @Setter(onMethod_ = @Autowired)
     private WebApplicationContext ctx;
     private MockMvc mockMvc;

     @Before //@Before가 적용된 메서드는 모든 테스트 전에 매번 실행되는 메서드.
     public void setup() {
          this.mockMvc = MockMvcBuilders.webAppContextSetup(ctx).build();
     } // MockMvc = 가짜 MVC (URL,파라미터 등을 가짜로 브라우저에서 사용하는 것처럼 하여 Controller를 실행 가능.)
     @Test
     public void testList() throws Exception {
          log.info("test");
          log.info(
               mockMvc.perform(MockMvcRequestBuilders.get("/board/list"))
                    .andReturn()
                    .getModelAndView()
                    .getModelMap()
          );
     }

     @Test
     public void testInsert() throws Exception {
          log.info("INSERT----------------------------------------------");
          String resultPage = mockMvc.perform(MockMvcRequestBuilders.post("/board/insert")
               .param("title","test new")
               .param("content","test new")
               .param("writer","test new")
               ).andReturn().getModelAndView().getViewName();

          log.info(resultPage);
     }

     @Test
     public void testGet() throws Exception {
          log.info("GET----------------------------------------------");
          mockMvc.perform(MockMvcRequestBuilders.get("/board/get")
               .param("bno","36")
               ).andReturn().getModelAndView().getViewName();
     }

     @Test
     public void testUpdate() throws Exception {
          log.info("UPDATE----------------------------------------------");
          String resultPage = mockMvc.perform(MockMvcRequestBuilders.post("/board/update")
               .param("bno","34")
               .param("title","new update")
               .param("content","new update")
               .param("writer","new update")
               ).andReturn().getModelAndView().getViewName();
          log.info(resultPage);
     }

     @Test
     public void testDelete() throws Exception {
          log.info("DELETE----------------------------------------------");
          String resultPage = mockMvc.perform(MockMvcRequestBuilders.post("/board/delete")
               .param("bno","33")
               ).andReturn().getModelAndView().getViewName();
          log.info(resultPage);
     }


}


>>> web.xml 하단에 붙여넣기


<!-- 한글깨짐 현상 처리 시작 -->
<filter>
     <filter-name>encodingFilter</filter-name>
     <filter-class>org.springframework.web.filter.CharacterEncodingFilter
     </filter-class>
     <init-param>
          <param-name>encoding</param-name>
          <param-value>UTF-8</param-value>
     </init-param>
</filter>

<filter-mapping>
     <filter-name>encodingFilter</filter-name>
     <url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 한글깨짐 현상 처리 끝 -->


>>> footer.jsp

<script> $(document).ready(function() { $('#dataTables-example').DataTable({ responsive: true }); $(".sidebar-nav") .attr("class","sidebar-nav navbar-collapse collapse") .attr("aria-expanded",'flase') .attr("style","height:1px") }); </script>

>>> list.jsp


<script type="text/javascript">
$(document).ready(function() {

     var result = '<c:out value="${result}"/>';
     console.log(result);
     checkModal(result);
     history.replaceState({},null,null);

          function checkModal(result) {
               if (result === '' || history.state) {
                    return;
               }
               if (parseInt(result) > 0) {
                    $(".modal-body").html(
                         "게시글 " + parseInt(result) + " 번이 등록되었습니다.");
               }else {
                    $(".modal-body").html("값이 전달되지 않습니다.");
               }
               $("#myModal").modal("show");
          }
     $("#regBtn").on("click",function() {
          self.location="/board/insert";
     });

});
</script>

>>> update.jsp

<script> $(document).ready(function(){ var formObj = $("form"); $('button').on("click",function(e){ e.preventDefault(); var operation = $(this).data("oper"); console.log(operation); if(operation==='delete'){ formObj.attr("action","/board/delete"); }else if(operation==='list'){ //move to list self.location="/board/list"; return; }formObj.submit(); }); $("p").click(function(){ $(this).hide(); }); }); </script>

< Spring 기초 >




>>> pom.xml
12라인 -> 5.0.7
64라인 -> 1.2.17
116라인 -> 4.12
139라인 -> 3.5.1
141라인 -> 1.8
142라인 -> 1.8
-----------------------------------------------------------------------------------------------------
>>> root-context.xml

<bean id="hikariConfig" class="com.zaxxer.hikari.HikariConfig">

     <property name="driverClassName"
          value="net.sf.log4jdbc.sql.jdbcapi.DriverSpy"></property>
     <property name="jdbcUrl"
          value="jdbc:log4jdbc:oracle:thin:@localhost:1521:XE"></property>
     <property name="username" value="db53"></property>
     <property name="password" value="123456"></property>

</bean>

<!-- HikariCP configuration -->
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource"
     destroy-method="close">
          <constructor-arg ref="hikariConfig" />
</bean>

<bean id="sqlSessionFactory"
     class="org.mybatis.spring.SqlSessionFactoryBean">
          <property name="dataSource" ref="dataSource">
</bean>

<mybatis-spring:scan
     base-package="com.jslhrd.mapper" />

<context:component-scan
     base-package="com.jslhrd.service">

-----------------------------------------------------------------------------------------------------
>>> buildpath
mysql-connector-java-8.0.28.jar
ojdbc8.jar
-----------------------------------------------------------------------------------------------------
>>>src/main/resources
log4jdbc.log4j2.properties 파일 생성
log4jdbc.spylogdelegator.name=net.sf.log4jdbc.log.slf4j.Slf4jSpyLogDelegator 내용입력
-----------------------------------------------------------------------------------------------------
>>>
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("file:src/main/webapp/WEB-INF/spring/root-context.xml")
@Log4j

@Setter(onMethod_ = @Autowired)
@Select("select sysdate from dual")

@Service // 계층 구조상 주로 비즈니스 영역을 담당하는 객체임을 표시하기 위해 사용함.
@Log4j
@AllArgsConstructor // 생성자를 만들고 자동으로 주입함.

<BoardController.java>
@Controller
@Log4j
@RequestMapping("/board/*")
@AllArgsConstructor

<BoardControllerTests.java>
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration({"file:src/main/webapp/WEB-INF/spring/root-context.xml",
               "file:src/main/webapp/WEB-INF/spring/appServlet/servlet-context.xml"})
@Log4j

>>>Tomcat 더블클릭 - Modules - Edit - controller 부분 지우기
-----------------------------------------------------------------------------------------------------
P.173
>>>Oracle 테이블

사용자 추가 후

system/123456

db53/123456


create user db53 identified by 123456;
grant connect,resource to db53;
grant create view to db53;

create sequence seq_board;

create table tbl_board(
bno number(10,0),
title varchar2(200) not null,
content varchar2(2000) not null,
writer varchar2(50) not null,
regdate date default sysdate,
updatedate date default sysdate
);

alter table tbl_board add constraint pk_board primary key(bno);

insert into tbl_board(bno,title,content,writer) values(seq_board.nextval,'테스트 제목','테스트 내용','user00');
insert into tbl_board(bno,title,content,writer) values(seq_board.nextval,'테스트 제목','테스트 내용','user01');
insert into tbl_board(bno,title,content,writer) values(seq_board.nextval,'테스트 제목','테스트 내용','user02');
insert into tbl_board(bno,title,content,writer) values(seq_board.nextval,'테스트 제목','테스트 내용','user03');
insert into tbl_board(bno,title,content,writer) values(seq_board.nextval,'테스트 제목','테스트 내용','user04');
insert into tbl_board(bno,title,content,writer) values(seq_board.nextval,'테스트 제목','테스트 내용','user05');
insert into tbl_board(bno,title,content,writer) values(seq_board.nextval,'테스트 제목','테스트 내용','user06');
insert into tbl_board(bno,title,content,writer) values(seq_board.nextval,'테스트 제목','테스트 내용','user07');
insert into tbl_board(bno,title,content,writer) values(seq_board.nextval,'테스트 제목','테스트 내용','user08');
insert into tbl_board(bno,title,content,writer) values(seq_board.nextval,'테스트 제목','테스트 내용','user09');
insert into tbl_board(bno,title,content,writer) values(seq_board.nextval,'테스트 제목','테스트 내용','user10');

-----------------------------------------------------------------------------------------------------
>>> BoardMapper.xml (@Select 어노테이션을 써도 되지만, 쿼리문이 복잡해질 경우 xml 파일로 관리하는게 유리하다 !!!)

src/main/resources -> com 폴더 -> jslhrd 폴더 -> mapper 폴더 생성 후, 그 안에 BoardMapper.xml 파일 생성
(com.jslhrd.mapper 패키지 명과 동일한 틀로)

<?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.jslhrd.mapper.BoardMapper">

     <!-- 부등호 들어가면 CDATA 처리 !!! -->

     <select id="getList" resultType="com.jslhrd.domain.BoardVO">
          <![CDATA[
               select * from tbl_board where bno > 0
          ]]>
     </select>

     <insert id="insert">
          insert into tbl_board(bno,title,content,writer) values(seq_board.nextval, #{title}, #{content}, #{writer})
     </insert>

     <insert id="insertSelectKey">
          <selectKey keyProperty="bno" order="BEFORE" resultType="long">
               select seq_board.nextval from dual
          </selectKey>
          insert into tbl_board(bno,title,content,writer) values(#{bno}, #{title}, #{content}, #{writer})
     </insert>

     <select id="read" resultType="com.jslhrd.domain.BoardVO">
          select * from tbl_board where bno = #{bno}
     </select>

     <update id="update">
          update tbl_board
          set title=#{title},
          content=#{content},
          writer=#{writer},
           updateDate=sysdate
          where bno=#{bno}
     </update>

     <delete id="delete">
          delete from tbl_board where bno=#{bno}
     </delete>

</mapper>

-----------------------------------------------------------------------------------------------------
src/main/resources -> com 폴더 -> jslhrd 폴더 -> mapper 폴더 생성 후, 그 안에 TimeMapper.xml 파일 생성
(com.jslhrd.mapper 패키지 명과 동일한 틀로)

<?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.jslhrd.mapper.TimeMapper">
     <select id="getTime2" resultType="string">
          select sysdate from dual
     </select>
</mapper>

--------------------------------------------------------------------------------------
>>> web.xml 인코딩

<!-- 한글깨짐 현상 처리 시작 -->
<filter>
     <filter-name>encodingFilter</filter-name>
     <filter-class>org.springframework.web.filter.CharacterEncodingFilter
     </filter-class>
     <init-param>
          <param-name>encoding</param-name>
          <param-value>UTF-8</param-value>
     </init-param>
</filter>

<filter-mapping>
     <filter-name>encodingFilter</filter-name>
     <url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 한글깨짐 현상 처리 끝 -->

--------------------------------------------------------------------------------------
>>> Modal 코드

<!-- Modal -->
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
     <div class="modal-dialog">
          <div class="modal-content">
               <div class="modal-header">
                    <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
                    <h4 class="modal-title" id="myModalLabel">Modal title</h4>
               </div>
               <div class="modal-body">
(modal)
               </div>
               <div class="modal-footer">
                    <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
                    <button type="button" class="btn btn-primary">Save changes</button>
               </div>
          </div>
          <!-- /.modal-content -->
     </div>
     <!-- /.modal-dialog -->
</div>
<!-- /.modal -->



https://mvnrepository.com/ <- dependency 검색
https://start.spring.io/ <- Spring Start Page

- 세팅이 반이다 !
- Test 중요 !
- build path (mysql-connector-java-8.0.28.jar , ojdbc8.jar)
- pom.xml 계속 수정

MVC

   (Model View Controller)

   Presentation - Client의 요청을 받아 Business에게 처리를 전달.
   Business - 실제 요청에 대한 처리를 담당. (Service)
   Persistence - Business의 처리에 따라 데이터베이스에 접근. 저장,조회,삭제 등 수행.

Layered Architecture


   각 계층은 자신의 계층이 갖는 책임에만 충실하도록 개발해야 한다.

View

Model Model

Presentation
@Controller

Model Model

Business
@Service

DTO Model

Persistence

DTO Model

DB
(Oracle)



프레젠테이션(웹) 계층의 CRUD 구현
Task URL Method Parameter From URL 이동
전체 목록 /board/list GET x x x
등록 처리 /board/insert POST 모든 항목 입력화면 필요 이동
조회 /board/get GET bno x x
수정 처리 /board/update POST 모든 항목 입력화면 필요 이동
삭제 목록 /board/delete POST bno 입력화면 필요 이동

mapper.insert(BoardVO) VS mapper.insertSelectKey(BoardVO)

p.192
    insert()의 경우 toString()의 출력결과 중 bno가 null값인 것을 확인.
    insertSelectKey()의 경우는 'select seq_board.nextval from dual'과 같은 쿼리가 먼저 실행되고,
    여기서 실행된 결과가 bno값으로 처리되는 것을 볼 수 있다.
    이런 방식의 등록 처리는 등록->목록 페이지로 이동할 때 bno의 값을 모달 피드백으로 활용하기 유용하다.


web.xml (한글 문제 UTF-8 필터 처리)

p.242
    1. 브라우저에서 한글이 깨져서 전송되는지를 확인.
    2. 문제가 없다면 스프링 MVC 쪽에서 한글을 처리하는 필터를 등록해야 한다.
    web.xml에 UTF-8 필터 코드를 작성. (본 페이지 매뉴얼의 최하단 참조)


재전송(redirect 처리)

p.245
    Browser (입력항목 전달)
    Controller (/board/list로 Redirect)
    Browser (/board/list 호출)
    Controller (목록 페이지 출력)

    위와 같이 board/list로 재전송을 하지 않는다면 새로고침을 통해서 동일한 내용을 계속해서 서버에 등록하게 됨 (도배 행위)
    따라서 CUD 작언 처리 완료 후 다시 동일한 내용을 전송할 수 없도록 아예 브라우저의 URL을 이동하는 방식을 이용.
    추가적으로, 모달이나 경고창 등을 통해 CUD 결과를 바로 알 수 있게 피드백 해줘야 한다.
    Controller에서 redirect 처리를 할 때 RedirectAttributes라는 특별한 타입의 객체를 이용.
    특히, addFlashAttribute()의 경우 일회성으로만 데이터를 전달하기 때문에 이런 처리에 적합하다.


페이지 앞/뒤로가기

p.257
   브라우저에서 게시글을 등록 후, 앞/뒤로가기를 하면 서버를 다시 호출하는 것이 아니라
   과거에 자신이 가진 모든 데이터를 활용하기 때문에 모달 창이 나타나게 된다.
   그러므로 스택 구조로 동작하는 window.history 객체를 조작하여
   board/list -> 다른 페이지로 이동할 때 board/list 스택에 모달이 필요 없다는 표시를 해둔다.


HTML의 데이터 속성 & <c:out/> 태그

p.262
   데이터 속성의 장점은 이전과 같이 hidden으로 태그를 숨겨두고 데이터를 저장할 필요가 없어졌다는 점이다.
   따라서 HTML 스크립트가 간결해짐. 또한 하나의 HTML 요소에는 여러 데이터 속성을 동시에 사용할 수도 있다.
   ex) data-oper='delete' -> 스크립트에서 var operation = $(this).data("oper"); 변수 지정.
   ex) if(operation === 'delete') 참일 경우 삭제 처리를 진행하도록 스크립트 코드 완성 (코드 참조)

   본 스프링 프로젝트는 넘어온 값을 웹에 표기할 때 <c:out value="${}" /> 태그를 사용.
   c:out 태그에는 디폴트 값으로 escapeXml옵션이 true로 설정되어 있다.
   이 속성은 사용자가 입력 칸에 스크립트 실행문을 작성해서 등록해도 HTML을 해석하지 않고 그대로 출력
   즉, ${} -> c:out태그를 사용하여 XSS(Cross-site Scripting) 등의 공격을 방지할 수 있다.



상위 폴더 패키지 클래스




src/main/java
com.jslhrd.sample Chef.java
Restaurant.java
com.jslhrd.controller BoardController.java (C)(Pre)
HomeController.java
com.jslhrd.mapper BoardMapper.java (I)(Per)
TimeMapper.java
com.jslhrd.domain BoardVO.java (C)(Per)
com.jslhrd.service BoardService.java (I)(Ser)
BoardServiceImpl.java (C)(Ser)




src/test/java
com.jslhrd.sample SmapleTests.java
com.jslhrd.controller BoardControllerTests.java (C)(Pre)

com.jslhrd.persistence
DataSourceTests.java
JDBCTests.java
TimeMapperTests.java
com.jslhrd.mapper BoardMapperTests.java (C)(Per)
com.jslhrd.service BoardServiceTests.java (C)(Ser)

src/main/resources
com/jslhrd/mapper BoardMapper.xml
TimeMapper.xml
. log4jdbc.log4j2.properties

webapp/WEB-INF/views
board list.jsp (전체 목록)
insert.jsp (게시글 등록)
get.jsp (상세 조회)
update.jsp (수정 및 삭제)
includes headr.jsp
footer.jsp

tbl_board
필드명 타입 설명
BNO NUMBER(10) 게시글 번호
TITLE VARCHAR(200) 제목
CONTENT VARCHAR(2000) 내용
WRITER VARCHAR(50) 작성자
HIT NUMBER(10) 조회수
REGDATE DATE 작성일
UPDATEDATE DATE 수정일