New Project(비동기, 댓글 추가)

Let's Just Go·2022년 7월 27일
0

Spring

목록 보기
15/26

Spring

New Project

Project Setting

  • pom.xml

    • 기본 maven 설정 변경
    • 버전 관리
    • 라이브러리 추가
    • maven update
    • 맥은 라이브러리가 좀 다름
  • 프로젝트 진행 순서

    • 와이어프레임 작성하여 요구사항 확인
    • 요구사항에 따른 DB 구현
    • 기능 구현
    • 기능에 맞게 프론트 구현
  • Table 생성

    • freeboard

      CREATE TABLE freeboard(
          bno NUMBER(10, 0),
          title VARCHAR2(300) NOT NULL,
          writer VARCHAR2(50) NOT NULL,
          content VARCHAR2(2000) NOT NULL,
          regdate DATE DEFAULT sysdate,
          updatedate DATE DEFAULT NULL
      );
      
      ALTER TABLE freeboard
      ADD CONSTRAINT freeboard_pk PRIMARY KEY(bno);
      
      CREATE SEQUENCE freeboard_seq
          START WITH 1
          INCREMENT BY 1
          MAXVALUE 5000
          NOCYCLE
          NOCACHE;

  • VO 객체 생성

    • VO 객체

      package com.spring.myweb.command;
      
      import java.sql.Timestamp;
      
      import lombok.Getter;
      
      import lombok.Setter;
      import lombok.ToString;
      
      @Getter
      @Setter
      @ToString
      public class FreeBoardVo {
      
      	private int bno;
      	private String title;
      	private String writer;
      	private String content;
      	private Timestamp regDate;
      	private Timestamp updateDate;
      	private boolean newMark;
      
      }

  • Page

    • page에 대한 정보들과 검색 정보들을 담은 객체 생성

      package com.spring.myweb.util;
      
      import lombok.Getter;
      import lombok.Setter;
      import lombok.ToString;
      
      @Getter
      @Setter
      @ToString
      public class PageVO {
      	
      	private int pageNum;
      	private int cpp;
      	
      	private String keyword;
      	private String condition;
      }

  • PageCreater

    • paging 알고리즘을 수행하기 위한 클래스

      package com.spring.myweb.util;
      
      import org.springframework.web.util.UriComponents;
      import org.springframework.web.util.UriComponentsBuilder;
      
      import lombok.Getter;
      import lombok.Setter;
      import lombok.ToString;
      
      @Getter
      @Setter
      @ToString
      public class PageCreator {
      	
      	private PageVO paging;
        private int articleTotalCount;
        // PageVO에서 받아오는 값들이며 이것들을 통해 연산 수행 
          
      	private int endPage;
      	private int beginPage;
      	private boolean prev;
      	private boolean next;
      	
      	private final int buttonNum = 5;
      	
      	private void calcDataOfPage() {
      		this.endPage = (int) Math.ceil(paging.getPageNum() / (double) this.buttonNum) * this.buttonNum;
      		
      		this.beginPage = (this.endPage - this.buttonNum) + 1;
      		
      		this.prev = (this.beginPage == 1) ? false : true;
      		
      		this.next = this.articleTotalCount <= (this.endPage * this.paging.getCpp()) ? false : true;
      		
      		if (!this.next) {
      			this.endPage = (int) Math.ceil(this.articleTotalCount / (double) this.paging.getCpp());
      		}
      	}
      	
         //컨트롤러가 총 게시물의 개수를 PageCreator에게 전달한 직후에 
         //바로 페이징 버튼 알고리즘이 돌아갈 수 있도록 setter를 커스텀.
         public void setArticleTotalCount(int articleTotalCount) {
            this.articleTotalCount = articleTotalCount;
            
            calcDataOfPage();
         }
         
      	// URI 파라미터를 쉽게 만들어주는 유틸 메서드 
      	public String makeURI(int page) {
      		UriComponents ucp = UriComponentsBuilder.newInstance().queryParam("page", page)
      															  .queryParam("cpp", paging.getCpp())
      															  .queryParam("keyword", paging.getKeyword())
      															  .queryParam("condition", paging.getCondition())
      															  .build();
      		// uri를 컨트롤러에서 받은 searchVO객체인 paging에서 받아서 구성 
      		return ucp.toUriString();
      	}
      	   
      }

  • Interface Mapper

    • Interface Mapper 생성

      package com.spring.myweb.freeboard.mapper;
      
      import java.util.List;
      
      import com.spring.myweb.command.FreeBoardVo;
      import com.spring.myweb.util.PageVO;
      
      public interface IFreeBoardMapper {
      
      	// 글 등록 
      	void regist(FreeBoardVo board);
      	
      	// 글 목록
      	List<FreeBoardVo> getList(PageVO page);
      	// 사용자가 선택한 페이지 정보와 검색 정보를 알 수 있는 PageVO 객체 매개변수 받음 
      	
      	// 총 게시물 수 
      	int getTotal(PageVO page);
      	// 검색에 따른 페이지 수 등이 다르므로 해당 정보를 가지고 있는 객체 매개변수로 받음 
      	
      	// 상세보기 
      	FreeBoardVo getContent(int bno);
      	
      	// 수정 
      	void update(FreeBoardVo board);
      	
      	// 삭제 
      	void delete(int bno);
      }

  • Mapper

    • resultMap을 통해 VO객체의 변수 이름과 DB의 컬럼 이름이 다른 것을 Mapping

    • typeAlias 태그를 통해 type의 이름이 길어지는 문제를 해결

      <?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.spring.myweb.freeboard.mapper.IFreeBoardMapper">
      	<!-- namespace는 어떤 interface를 참조하는지 지정  -->
      	
      	<resultMap type="FreeBoardVO" id="boardMap">
      		<!-- mybatis-config에서 별칭을 지정했으므로 type에 별칭 지정 -->
      		<!-- 해당 위치에 있는 객체와 db컬럼을 비교한다는 뜻 -->
      		
      		<result property="regDate" column="regdate"/>
      		<result property="updateDate" column="updatedate"/>
      		<!-- VO객체의 변수명과 db이름이 다른것을 맞춰줌 -->
      	
      	</resultMap>
      	
      	<!-- 동적 sql  -->
      	<sql id="search">
            
      		<if test="condition == 'title'">
      			WHERE title LIKE '%'||#{keyword}||'%'
      		</if>
      		
      		<if test="condition == 'content'">
      			WHERE content LIKE '%'||#{keyword}||'%'
      		</if>
      		
      		<if test="condition == 'writer'">
      			WHERE writer LIKE '%'||#{keyword}||'%'
      		</if>
      		
      		<if test="condition == 'titleContent'">
      			WHERE title LIKE '%'||#{keyword}||'%'
      			OR content LIKE '%'||#{keyword}||'%'
      		</if>
      		
      	</sql>
      	
      	<insert id="regist" >
      		INSERT INTO freeboard(bno, title, writer, content)
      		VALUES(freeboard_seq.NEXTVAL, #{title}, #{writer}, #{content})
      		<!-- 변수명만 제대로 작성하면 insert 끝 -->
      	</insert>
      	
      	<select id="getList" resultMap="boardMap" >
      		<!-- interface의 getList라는 메서드의 sql문이며 참고할 것은 baordMap을 통해 참고 -->
      		<!-- resultType="com.spring.myweb.command.FreeBoardVO" -->
      		
      		SELECT * FROM 
      		(SELECT ROWNUM as rn, tbl.* FROM 
          		(SELECT * FROM freeboard 
          		<include refid="search" />
          		ORDER BY bno DESC) tbl)
          		<![CDATA[ 
      		WHERE rn > (#{pageNum} -1 ) * #{cpp}
      		AND rn <= #{pageNum} * #{cpp}
      		]]>
      	</select>
      	
      	<select id="getTotal" resultType="int">
      		SELECT count(*) FROM freeboard 
      		<!-- 동적 sql paging -->
      		<include refid="search"/>
      	</select>
      	
      	<select id="getContent" resultType="FreeBoardVO">
      		<!-- mybatis-config에서 별칭을 지정했으므로 type에 별칭 지정 -->
      	
      		SELECT * FROM freeboard 
      		WHERE bno = #{bno}
      	</select>
      	
      	<update id="update">
      		UPDATE freeboard SET 
      		writer = #{writer}, title = #{title}, content = #{content}, updatedate = sysdate
      		WHERE bno = #{bno}
      	</update>
      	
      	<delete id="delete">
      		DELETE FROM freeboard 
      		WHERE bno = #{bno}
      	</delete>
      	
      	
      </mapper>

  • Servlet-config

    • servlet-config.xml

      <?xml version="1.0" encoding="UTF-8"?>
      <beans:beans xmlns="http://www.springframework.org/schema/mvc"
      	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      	xmlns:beans="http://www.springframework.org/schema/beans"
      	xmlns:context="http://www.springframework.org/schema/context"
      	xmlns:jdbc="http://www.springframework.org/schema/jdbc"
      	xmlns:mybatis-spring="http://mybatis.org/schema/mybatis-spring"
      	xsi:schemaLocation="http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-4.3.xsd
      		http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd
      		http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring-1.2.xsd
      		http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
      		http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
      
      	<!-- 자동으로 컨트롤러와 매핑 메서드 탐색 (handler mapping과 handler adapter bean 등록)-->
      	<annotation-driven />
      
      	<!-- css, img, js ... 의 파일 경로가 복잡할 때 많이 사용  -->
      	<resources mapping="/resources/**" location="/resources/" />
      	<resources mapping="/img/**" location="/resources/img/" />
      	<resources mapping="/css/**" location="/resources/css/" />
      	<resources mapping="/fonts/**" location="/resources/fonts/" />
      	<resources mapping="/js/**" location="/resources/js/" />
      
      	<!-- 컨트롤러가 리턴한 문자열 앞, 뒤에 적절한 경로를 붙여서 화면을 응답할 수 있도록 도와주는 viewResolver -->
      	<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
      		<beans:property name="prefix" value="/WEB-INF/views/" />
      		<beans:property name="suffix" value=".jsp" />
      	</beans:bean>
      	
      	<!-- annotation으로 등록된 클래스 객체들을 탐색해 주는 설정 태그이며 
      		 base-package에다가 탐색한 패키지 경로를 작성하면 하위 패키지까지 모두 검색  -->
      	<!-- 자동 bean 등록  -->
      	<context:component-scan base-package="com.spring.myweb" />
      	
      </beans:beans>

  • DB-config

    • Connection Pool은 자주 바뀌지 않으므로 따로 파일을 만들어서 사용

    • .properties라는 파일을 사용해서 생성한 파일에 DB 연결에 관련된 정보들을 입력

    • 외부에 DB와 관련된 정보를 노출시키지 않을 수 있는 장점이 존재

    • 정보의 변경이 발생했을 때 해당 파일의 정보만 변경하면 되기 때문에 유지보수 편리

      ## local oracle 
      ds.driverClassName = oracle.jdbc.driver.OracleDriver
      ds.url = jdbc:oracle:thin:@localhost:1521:xe
      ds.username = user아이디
      ds.password = user비밀번호
      
      ## local mysql (if ~)
      mydb.driverClassName = com.mysql.cj.jdbc.driver
      mydb.url = jdbc:mysql://localhost:3306/spring(db name)
      mydb.username = user아이디
      mydb.password = user비밀번호

  • root-config.xml (DB연결 사항)

    • namespace에 jdbc, mybatis-spring 선택

    • mybatis 동작을 위한 bean 등록

    • mapper의 type이 길어지는 문제를 alias를 지정하여 간편하게 작성 가능

    • 위에서 작성한 properties파일을 등록해야 동작 가능

      <?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:jdbc="http://www.springframework.org/schema/jdbc"
      	xmlns:mvc="http://www.springframework.org/schema/mvc"
      	xmlns:mybatis-spring="http://mybatis.org/schema/mybatis-spring"
      	xsi:schemaLocation="http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-4.3.xsd
      		http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
      		http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring-1.2.xsd
      		http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
      
      	<!-- 프로젝트를 구성하다 보면 자주 변경되지 않는 설정 파일들이나 공통 정보들에 대한 
      		 내용이 존재하게 되고 그 내용은 한 번 지정되면 잘 바뀌지 않음 
      		 이러한 경우에는 .properties라는 파일을 사용하여 텍스트 형식으로 간단히 지정하고 
      		 필요할 때 불러와서 사용하는 방식 사용  -->
      	
      	<!-- 외부에 따로 설정한 설정 파일을 참조하는 곳에서 찾아서 빈으로 등록하여 사용하는 클래스  -->
      	<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
      		<property name="location" value="classpath:/db-config/Hikari.properties" />
      		<!-- 경로 지정  -->
      		
      	</bean>
      	
      	<!-- JDBC, DB 관련 빈을 등록하고 관리하는 설정 파일 -->
      	<!-- 히카리 커넥션 풀 빈 등록 -->
      	<bean id="hikariConfig" class="com.zaxxer.hikari.HikariConfig">
      		<!-- property는 객체의 setter처럼 값 주입 -->
      		<property name="driverClassName"
      			value="${ds.driverClassName}" />
      		<property name="jdbcUrl"
      			value="${ds.url}" />
      		<property name="username" value="${ds.username}" />
      		<property name="password" value="${ds.password}" />
      		<!-- 가져온 파일 안에 적혀있는 내용르 작성하면 됨 (db종류에 따라) 
      			 mysql도 가능   -->
      	</bean>
      	
      
      	<!-- 히카리 데이터 소스 빈 등록 -->
      	<bean id="ds" class="com.zaxxer.hikari.HikariDataSource">
      		<constructor-arg ref="hikariConfig" />
      		<!-- 히카리 데이터 소스에 히카리 커넥션 풀 주입 -->
      	</bean>
      
      	<!-- mybatis SQL 동작을 위한 핵심 객체 SqlSessionFactory 클래스 빈 등록 -->
      	<bean id="sqlSessionFactory"
      		class="org.mybatis.spring.SqlSessionFactoryBean">
      		<property name="dataSource" ref="ds" />
      		
      	 <property name="mapperLocations" value="classpath:/mappers/*Mapper.xml"/> 
      
      	</bean>
      
      	<!-- 지정한 패키지를 스캔하여 존재하는 Mapper 인터페이스를 bean type으로 등록 -->
      	<!-- sqlSessionFactory가 xml파일을 클래스로 변환하여 bean으로 등록하려는 시도를 할 때 타입을 지정해야 하기 
      		때문 -->
      	<mybatis-spring:scan
      		base-package="com.spring.myweb.freeboard.mapper" />
      	<!-- Mapper인터페이스가 어디에 있는지 경로 지정 -->
      </beans>

  • Controller

    • 사용자의 요청을 처리

      package com.spring.myweb.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.PathVariable;
      import org.springframework.web.bind.annotation.PostMapping;
      import org.springframework.web.bind.annotation.RequestBody;
      import org.springframework.web.bind.annotation.RequestMapping;
      import org.springframework.web.bind.annotation.RequestParam;
      import org.springframework.web.bind.annotation.ResponseBody;
      import org.springframework.web.servlet.mvc.support.RedirectAttributes;
      
      import com.spring.myweb.command.FreeBoardVO;
      import com.spring.myweb.freeboard.service.IFreeBoardService;
      import com.spring.myweb.util.PageCreator;
      import com.spring.myweb.util.PageVO;
      
      @Controller
      // controller bean 등록 
      @RequestMapping("/freeBoard")
      public class FreeBoardController {
      
      	@Autowired
      	private IFreeBoardService service;
      	
      	// 글 작성 
      	@GetMapping("/freeRegist")
      	public String freeRegi() {
      		System.out.println("/freeBoard/freeRegist : GET");
      		System.out.println("작성 페이지로 이동");
      		
      		return "/freeBoard/freeRegist";
      	}
      	
      
      	@PostMapping("/freeRegist")
      	public String freeRegi(FreeBoardVO board, RedirectAttributes ra) {
      		System.out.println("/freeBoard/freeRegist : POST");
      		System.out.println("게시물 값 가져오는지 확인" + board.toString());
      		
      		service.regist(board);
      		ra.addFlashAttribute("msg", "정상 등록 처리되었습니다.");
      		return "redirect:/freeBoard/freeList";
      	}
      	
      	
      	// 목록 
      	@GetMapping("/freeList")
      	public String freeList(PageVO page, Model model) {
      		System.out.println("/freeboard/freeList : GET");
      		System.out.println("page : " + page.toString());
      		// 페이지 들고 오는지 확인 
      		
      		List<FreeBoardVO> allList = service.getList(page);
      		model.addAttribute("boardList", allList);
      		// 모델에 데이터를 담아서 보내줌
      		
      		PageCreator pcv = new PageCreator();
      		pcv.setPaging(page);
      		pcv.setArticleTotalCount(service.getTotal(page));
      		System.out.println("pcv 객체 확인 : " + pcv.toString());
      		
      		model.addAttribute("pcv", pcv);
      		// 모델에 데이터를 담아서 보내줌
      		return "/freeBoard/freeList";
      		
      	}
      	
      	// 상세보기 
      	@GetMapping("/freeDetail/{bno}")
      	public String freeContent(@PathVariable int bno, Model model, PageVO page) {
      		// 파리미터 이름과 변수 이름이 같으면 ()안적고 넣을 수 있음
      		// 파라미터 명이 bno라는 것을 받아와서 상세보기 구현 
      		System.out.println("/freeBoard/freeDetail : GET");
      		System.out.println("게시물 상세보기 , 게시물 번호 가져오는지 확인 : " + bno);
      		
      		System.out.println("페이지 정보 가지고 오는지 확인 : " + page.toString());
      		model.addAttribute("p", page);
      		
      		FreeBoardVO board = service.getContent(bno);
      		System.out.println("게시물 DB에서 가져오는지 확인 : " + board.getBno());
      		model.addAttribute("board", board);
      		return "/freeBoard/freeDetail";
      	}
      	
      	// 게시물 수정
      	@GetMapping("/freeModify")
      	public String freeMoveModi(@RequestParam("bno") int bno, Model model) {
      		System.out.println("/freeBoard/freeModify : GET");
      		System.out.println("게시물 수정 , 게시물 번호 가져오는지 확인 : " + bno);
      		FreeBoardVO board = service.getContent(bno);
      		
      		model.addAttribute("board", board);
      		return "/freeBoard/freeModify";
      	}
      	
      	// 게시물 수정 
      	@PostMapping("/freeModify")
      	public String freeModi(FreeBoardVO upBoard, RedirectAttributes ra) {
      		System.out.println("/freeBoard/freeModify : POST");
      		System.out.println("수정하고 싶은 값 받오는지 확인 : " + upBoard.toString());
      		service.update(upBoard);
      		
      		ra.addFlashAttribute("msg", "수정완료");
      		return "redirect:/freeBoard/freeDetail/"+upBoard.getBno()+"/";
      	}
      	
      	// 게시물 삭제
      	@PostMapping("/freeDelete")
      	public String freeDelete(@RequestParam("bno") int bno, RedirectAttributes ra) {
      		System.out.println("/freeBoard/freeDelete : POST");
      		System.out.println("게시물 번호 가져오는지 확인 : " + bno);
      		service.delete(bno);
      		
      		ra.addFlashAttribute("msg", "게시글이 정상적으로 삭제되었습니다.");
      		return "redirect:/freeBoard/freeList";
      		// 다시 목록으로 돌아가게 해줌
      	}
      	
      }
profile
안녕하세요! 공부한 내용을 기록하는 공간입니다.

0개의 댓글