SpringBoot - 01

월요일좋아·2022년 11월 9일
0

SpringBoot

목록 보기
1/10

< 스프링 부트와 레거시 스프링의 차이점은 1개! >

  • 스프링 부트 : 설정 자동화
  • 레거시 스프링 : 사용자 직접 설정

STS에서 JAVA EE가 없다면

help -> Install New Software -> Next->Finish, 종료 후 재시작

스프링부트 프로젝트 생성


War : 톰캣서버 실행해야 됨
Jar : 톰캣서버 포함이라서 그냥 동작

처음 생성하고나서 Import하는 데 시간이 걸림!

웹에서 스프링부트 프로젝트 생성

https://start.spring.io/


스프링부트 시작하기




↑ 목록 간편하게 보기

↑ Controller 패키지 -> New -> Java Class

↑ 클래스 생성


↑ Run

↑ 위 화면이 뜨면 스프링부트가 잘 실행된 것이다.



인텔리제이에서 스프링 프로젝트 생성


STS에서와 동일하게 패키지 생성 -> 패키지 내부에 자바 클래스 생성

실행버튼

이대로 하면 한가지 문제가생김. 개발 후 소스 내용을 수정했을때 바로 적용되게 못함(서버 재시작해야 적용됨)

  1. 구성편집
  2. 옵션 수정 우클릭
  3. 클래스 및 리소스 업데이트(프레임 비활성화시, Update 작업 시)


@RestController, @Controller

  • 사용자의 요청을 받는 일을 함 -> 요청에 따른 작업을 한 후 응답을 함

@RequestMapping

  • 사용자가 요청하는 웹 주소를 명시해두는것(이 주소로 들어오면 아래 코드를 실행하겠다~)

board1 프로젝트 시작

  1. 새 프로젝트 생성
  2. 종속성 추가 (버전 : 2.6.13)





controller 패키지 생성, BoardController 클래스 생성
이번 프로젝트는 첫 실행시 오류남 -> 처음 종속성 주가에 마이바티스, MySQL 을 넣어줬는데 그걸 연결하는걸 안해줬때문
일단 build.gradle에서 해당부분 두가지 주석처리 후 재실행 해주자
-> 기본 준비 완료


  1. @RestController를 @Controller로 수정
package com.bitc.board.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class BoardController {

    @RequestMapping("/")
    public String index() throws Exception {
        return "index";
    }
}
  1. src - resource - templates - 우클릭 : 새로 만들기 - index.html
<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/css/bootstrap.min.css">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.1/jquery.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/js/bootstrap.bundle.min.js"></script>
</head>
<body>
<div class="container mt-5">
  <h1>처음 만드는 스프링부트 프로젝트</h1>
</div>
</body>
</html>


MVC 패턴

스프링은 MVC 패턴을 사용함.
M : Model - DB
V : View - 클라이언트 화면
C : Controller - 사용자 요청에 대한 응답 ★

DB 연결

  1. 설정
    application properties ->
server.port=8080

spring.datasource.hikari.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.hikari.jdbc-url=jdbc:mysql://localhost:3306/javadb?useUnicode=true&characterEncoding=utf-8&serverTimeZone=UTC
spring.datasource.hikari.username=test1
spring.datasource.hikari.password=java505
spring.datasource.hikari.connection-test-query=SELECT 1

mybatis.configuration.map-underscore-to-camel-case=true
  1. 패키지 및 클래스파일 생성
  • (configuration - DatabaseConfiguration)

  • build.gradle에서 주석처리했던 두줄 주석 해제

  • DatabaseConfiguration.java

package com.bitc.board.configuration;


import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

import javax.sql.DataSource;

@Configuration
@PropertySource("classpath:/application.properties")
public class DatabaseConfiguration {
    @Autowired
    private ApplicationContext applicationContext;

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.hikari")
    public HikariConfig hikariConfig() {
        return new HikariConfig();
    }

    @Bean
    public DataSource dataSource() throws Exception {
        DataSource dataSource = new HikariDataSource(hikariConfig());
        System.out.println(dataSource.toString());
        return dataSource;
    }

    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
//        주의! Location이 아니라 Locations 임, getResources 도 마찬가지로 getResource 아님
        // 스프링에서 별 두개(**)는 모든 파일/모든 폴더 등, 모든! 을 뜻함.
        // sql-*.xml는  sql-아무 파일명.xml 형식에 맞는 파일을 들고옴
        sqlSessionFactoryBean.setMapperLocations(applicationContext.getResources("classpath:/mapper/**/sql-*.xml"));
        sqlSessionFactoryBean.setConfiguration(mybatisConfig());

        return sqlSessionFactoryBean.getObject();
    }

    @Bean
    public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }

    @Bean
    @ConfigurationProperties(prefix = "mybatis.configuration")
    public org.apache.ibatis.session.Configuration mybatisConfig() {
        return new org.apache.ibatis.session.Configuration();
    }
}
  • src - resource 우클릭 - 새로만들기 - 경로 (name : mapper)
    mapper 우클릭 - 새로만들기 - XML구성파일 - Spring구성 - name : sql-board

  • sql-board.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.bitc.board.mapper.BoardMapper">
    <select id="selectBoardList" resultType="com.bitc.board.dto.BoardDto">
        <![CDATA[
        -- 게시글 목록 조회
        SELECT idx, title, user_id, create_dt, hit_cnt FROM t_board
        WHERE deleted_yn = 'N'
        order by idx DESC -- 세미콜론 빼야됨
        ]]>
    </select>

    <select id="selectBoardDetail" parameterType="int" resultType="com.bitc.board.dto.BoardDto">
        <![CDATA[
        -- 게시글 상세 --
        SELECT idx, title, contents, user_id, create_dt, hit_cnt, update_dt, hit_cnt FROM t_board
        WHERE idx  = #{idx} -- 세미콜론 빼야됨 --
        ]]>
    </select>

    <insert id="insertBoard" parameterType="com.bitc.board.dto.BoardDto">
        <![CDATA[
        -- 게시글 등록 --
        INSERT INTO t_board (title, contents, user_id, pwd, create_dt)
        VALUES (#{title}, #{contents}, #{pwd}, #{user_id}, NOW()) -- 세미콜론 빼야됨 --
        ]]>
    </insert>

    <update id="updateBoard" parameterType="com.bitc.board.dto.BoardDto">
        <![CDATA[
        -- 게시글 수정 --
        UPDATE t_board
        SET
            title = #{title},
            contents = #{contents},
            Update_dt = NOW()
        WHERE
            idx = #{idx} -- 세미콜론 빼야됨 --
        ]]>
    </update>

    <update id="deleteBoard" parameterType="int">
        <![CDATA[
        -- 게시글 삭제 --
        UPDATE t_board SET deleted_yn = 'Y'
        WHERE idx = #{idx} -- 세미콜론 빼야됨 --
        ]]>
    </update>

    <update id="updateHitCount" parameterType="int">
        <![CDATA[
        -- 조회수 카운트 --
        UPDATE t_board SET hit_cnt = hit_cnt + 1
        WHERE idx = #{idx} -- 세미콜론 빼야됨 --
        ]]>
    </update>

</mapper>
  • templates 폴더 밑에 boardList, boardDetail, boardWrite 필요
  • SQL문 참고
SELECT * FROM javadb.t_board;

-- 게시글 목록 조회
SELECT idx, title, user_id, create_dt, hit_cnt FROM t_board 
WHERE deleted_yn = 'N' 
order by idx DESC;

-- 게시글 상세
SELECT idx, title, contents, user_id, create_dt, hit_cnt, update_dt, hit_cnt FROM t_board
WHERE idx  = 2;

-- 게시글 등록
INSERT INTO t_board (title, contents, user_id, pwd, create_dt)
VALUES ('테스트 제목', '테스트 내용', 1234, 'test2', NOW());

-- 게시글 수정
UPDATE t_board SET title = '수정된 테스트 제목 4', contents = '수정된 테스트 내용 4', Update_dt = NOW()
WHERE idx = 2;

-- 게시글 삭제
UPDATE t_board SET deleted_yn = 'N'
WHERE idx = 2;

-- 조회수 카운트
UPDATE t_board SET hit_cnt = hit_cnt + 1
WHERE idx = 2; 
  • com.bitc.board 에 경로 추가
    (dto, mapper, service)

  • com.bitc.board -> controller -> BoardController.java 파일에 코드 추가

package com.bitc.board.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class BoardController {

    @RequestMapping("/")
    public String index() throws Exception {
        return "index";
    }

//-- 추가한 코드
    @RequestMapping("/board/openBoardList.do")
    public ModelAndView openBoardList() throws Exception {
        ModelAndView mv = new ModelAndView("board/boardList");

        return mv;
    } //--
}
  • resources -> templates -> 경로 추가 : board -> board 파일에 boardList.html 파일 생성

  • boardList.html

나중에 입력
  • dto 경로 내부에 BoardDto.java 생성
package com.bitc.board.dto;

import lombok.Data;

@Data
public class BoardDto {
    // 컬럼명과 동일하게 써주면 됨
    // (언더바 들어가는 컬럼은 _를 지우고 그 뒤의 첫 글자를 대문자로 바꿔줘야함)
    private int idx;
    private String title;
    private String contents;
    private String userId;
    private String pwd;
    private String createDt;
    private String updateDt;
    private int hitCnt;
}
  • 중간 결과 확인 : localhost:8080/board/openBoardList.do 검색

  • BoardController 코드 추가

package com.bitc.board.controller;

import com.bitc.board.dto.BoardDto;
import com.bitc.board.service.BoardService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import java.util.List;

@Controller
public class BoardController {
    @Autowired
    private BoardService boardService;

    @RequestMapping("/")
    public String index() throws Exception {
        return "index";
    }

    @RequestMapping("/board/openBoardList.do")
    public ModelAndView openBoardList() throws Exception {
        ModelAndView mv = new ModelAndView("board/boardList");

        List<BoardDto> dataList = boardService.selectBoardList();
        mv.addObject("dataList", dataList);
        return mv;
    }
}
  • BoardService에서 Alt + Enter -> 인터페이스 만들기

  • boardService 경로에 BoardService 인터페이스 생성 확인됨

  • boardService.java

package com.bitc.board.service;

import com.bitc.board.dto.BoardDto;

import java.util.List;

public interface BoardService {

    List<BoardDto> selectBoardList() throws Exception;
}
  • BoardServiceImpl.java
package com.bitc.board.service;

import com.bitc.board.dto.BoardDto;
import com.bitc.board.mapper.BoardMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class BoardServiceImpl implements BoardService {
    // 빨간밑줄 -> 구현... 클릭 -> OK

    @Autowired
    private BoardMapper boardMapper;
    @Override
    public List<BoardDto> selectBoardList() throws Exception {
        return boardMapper.selectBoardList();
    }
}
  • BoardMapper 인터페이스 생성
  • BoardMapper.java
package com.bitc.board.mapper;

import com.bitc.board.dto.BoardDto;
import org.apache.ibatis.annotations.Mapper;

import java.util.List;

@Mapper
public interface BoardMapper {
    List<BoardDto> selectBoardList() throws Exception;
}


  • boardList.html
<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/css/bootstrap.min.css">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.1/jquery.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/js/bootstrap.bundle.min.js"></script>
</head>
<body>
<div class="container">
    <div class="row">
        <div class="col-sm">
            <table class="table table-hover table-striped">
                <colgroup>
                    <col width="10%">
                    <col width="*">
                    <col width="15%">
                    <col width="15%">
                    <col width="15%">
                </colgroup>
                <thead>
                    <tr>
                        <th scope="col">글번호</th>
                        <th>제목</th>
                        <th>글쓴이</th>
                        <th>등록일</th>
                        <th>조회수</th>
                    </tr>
                </thead>
                <tbody>
                    <tr th:if="${#lists.size(dataList)} > 0" th:each="list : ${dataList}">
                        <td th:text="${list.idx}"></td>
                        <td>
                            <a href="/board/openBoardDetail.do?idx=" th:attrappend="href=${list.idx}" th:text="${list.title}"></a>
                        </td>
                        <td th:text="${list.userId}"></td>
                        <td th:text="${list.createDt}"></td>
                        <td th:text="${list.hitCnt}"></td>
                    </tr>
                    <tr th:unless="${#lists.size(dataList)} > 0">
                        <td th:cols="5">조회된 결과가 없습니다.</td>
                    </tr>
                </tbody>
            </table>
        </div>
    </div>
</div>
</body>
</html>
  • 중간 확인

    • 주의할 점!
      html파일에서 바로 실행하면 localhost주소가 이상한 곳으로 가서 실행이 제대로 안됨. url창에 직접 localhost:8080 + 파일명 써주기
  • 데이터 이동 순서

    • 처음
      • 클라이언트(웹브라우저)에서 서버(localhost:8080)로 요청(/board/openBoardList.do)
      • 실제 배포할때 서버포트는 :80 임
      • 컨트롤러가 요청을 받음
      • 컨트롤러 : @Controller or @RestController 가 있는 파일
      • 여러 컨트롤러 파일 중, 요청한 주소(/board/openBoardList.do)와 일치하는 메서드가 있는 컨트롤러를 찾아감
      • 해당 메서드 내부에는 boardServive가 존재함
      • boardServive -> selectBoardList 실행
      • service는 인터페이스로 만들어져있기때문에 추상메서드가 존재(List<>~)
      • BoardServiceImpl 내부의 selectBoardList메서드 : boardMapper.~ 리턴
      • boardMapper로 가보니 인터페이스로 되어있고 그 내부에 List타입의 BoardDto, selectBoardList 추상메서드 존재 -> 이것에 대한 구현체가 xml 파일이다.
      • 웹브라우저(클라이언트) ---요청:(/board/openBoardList.do)---> controller -> service -> ORM(->mybatis) -> DB -> ORM -> service -> controller -> 데이터 처리 / 가공 -> 사용자에게 보여줄 view파일 지정 -> view(boardList.html)
  • view는 무조건 templates 파일 밑에 있음!

  • service와 ORM(mapper) 모두 인터페이스임

  • xml에는 boardMapper에 있는 id와 동일한거 쓰면 안됨

  • 마이바티스 사용법 참고 : https://mybatis.org/mybatis-3/getting-started.html#exploring-mapped-sql-statements

어노테이션

  • @Controller : 사용자가 웹브라우저를 통하여 어떠한 요청을 할 경우 해당 요청을 처리하기 위한 비즈니스 로직을 가지고 있는 어노테이션.
    클래스에 해당 어노테이션을 사용하면 해당 클래스는 사용자 요청을 처리하기 위한 클래스라는 것을 스프링 프레임워크에 알림

  • @Autowired : 사용자가 해당 타입의 객체를 생성하는 것이 아니라 스프링프레임워크가 해당 타입의 객체를 생성하고, 사용자는 이용만 하도록 하는 어노테이션

  • @RequestMapping : 사용자가 웹브라우저를 통해서 접속하는 실제 주소와 메서드를 매칭하기 위한 어노테이션

    • value 속성 : 사용자가 접속할 주소 설정, 2개 이상의 주소를 하나의 메서드와 연결하려면 {주소1, 주소2, ...} 형태로 사용, value 속성만 사용할 경우 생략 가능 (value="주소" --(생략)--> "주소")
    • method 속성 : 클라이언트에서 서버로 요청 시 사용하는 통신 방식을 설정하는 속성 (GET/POST),RequestMethod 타입을 사용, Restful 방식을 사용할 경우 GET/POST/UPDATE/DELETE 를 사용할 수 있음, 기본값 = GET
      @RequestMapping("/board/openBoardList.do", method = RequestMethod.GET/POST))
  • @Service : 해당 파일이 서비스 Interface 파일(= 컨트롤러에서 @Autowired로 만들어진것)을 구현하는 구현체라는 것을 알려주는 어노테이션

  • @Mapper : mybatis orm을 사용하여 xml 파일과 연동된 인터페이스임을 알려주는 어노테이션

sql-*.xml 파일 설명

<?xml version="1.0" encoding="UTF-8"?>
<!-- sql-board.xml : 마이바티스 실행을 위해 필요한 파일 -->

<!-- mybatis SQL 매핑 파일을 뜻하는 지시문 -->
<!DOCTYPE mapper PUBLIC "-//mybatis.org/DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!-- namespace 속성에 지정한 패키지명을 가지고 있는 파일과 아래의 xml 구문을 연동한다는 의미 -->
<!-- ★★★ xml 파일이기 때문에 스프링 프로젝트의 전체 구조를 모르기 때문에 전체 패키지 명을 다 입력해야 함 ★★★ -->
<mapper namespace="com.bitc.board.mapper.BoardMapper">
<!--    실제 sql 쿼리문을 입력하는 부분 -->
<!--        id 속성 : 위에서 지정한 파일에 존재하는 메서드 명과 동일하게 입력해야 함(오타->오류). 오버로딩을 지원하지 않음   -->
<!--     resultType : 지정한 메서드의 반환값, 자바 기본 타입은 그대로 입력 가능(ex-int, String 등),                          -->
<!--                  사용자가 지정한 데이터 타입은 xml 파일에서 인식하지 못하기 때문에 전체 패키지명을 다 입력해야 함       -->
<!--  parameterType : 지정한 메서드의 매개변수가 가지고 있는 데이터 타입, 자바 기본 타입은 그대로 입력,                      -->
<!--                  사용자가 지정한 데이터 타입은 전체 패키지명을 다 입력해야 함.                                          -->
<!--      변수 선언 : PreparedStatement 방식을 사용하여 지정한 위치에 데이터를 입력하기 위해서 #{변수명} 형태를 사용         -->
<!--                  매개변수가 기본 타입을 경우 mapper 파일의 메서드의 변수명을 그대로 사용 가능                           -->
<!--                  @Param 어노테이션을 사용하여 매개변수의 이름을 지정할 수 있음                                          -->
<!--                  매개변수가 사용자 지정 타입일 경우(ex-dto) 해당 타입의 멤버 변수명을 그대로 사용                       -->
    <select id="selectBoardList" resultType="com.bitc.board.dto.BoardDto">
        <![CDATA[
        SELECT idx, title, user_id, create_dt, hit_cnt FROM t_board
        WHERE deleted_yn = 'N'
        order by idx DESC
        ]]>
    </select>

    <select id="selectBoardDetail" parameterType="int" resultType="com.bitc.board.dto.BoardDto">
        <![CDATA[
        SELECT idx, title, contents, user_id, create_dt, hit_cnt, update_dt, hit_cnt FROM t_board
        WHERE idx  = #{idx}
        ]]>
    </select>

    <insert id="insertBoard" parameterType="com.bitc.board.dto.BoardDto">
        <![CDATA[
        INSERT INTO t_board (title, contents, user_id, pwd, create_dt)
        VALUES (#{title}, #{contents}, #{pwd}, #{user_id}, NOW())
        ]]>
    </insert>

    <update id="updateBoard" parameterType="com.bitc.board.dto.BoardDto">
        <![CDATA[
        UPDATE t_board
        SET
            title = #{title},
            contents = #{contents},
            Update_dt = NOW()
        WHERE
            idx = #{idx}
        ]]>
    </update>

    <update id="deleteBoard" parameterType="int">
        <![CDATA[
        UPDATE t_board SET deleted_yn = 'Y'
        WHERE idx = #{idx}
        ]]>
    </update>

    <update id="updateHitCount" parameterType="int">
        <![CDATA[
        UPDATE t_board SET hit_cnt = hit_cnt + 1
        WHERE idx = #{idx}
        ]]>
    </update>

</mapper>

BoardDto 설명

package com.bitc.board.dto;

import lombok.Data;

//  @Data : lombok 라이브러리에서 지원하는 어노테이션으로,
//          해당 클래스의 멤버 변수에 대한 getter/setter/toString() 메서드를 자동으로 생성하는 어노테이션
//          @Getter, @Setter, @ToString 어노테이션을 모두 사용한것과 같은 효과임
@Data

//@Getter : 자동 Getter 생성(@Data 없애고 사용 가능)
//@Setter : 자동 Setter 생성(@Data 없애고 사용 가능)
public class BoardDto {
    // 컬럼명과 동일하게 써주면 됨
    // (언더바 들어가는 컬럼은 _를 지우고 그 뒤의 첫 글자를 대문자로 바꿔줘야함)
    private int idx;
    private String title;
    private String contents;
    private String userId;
    private String pwd;
    private String createDt;
    private String updateDt;
    private int hitCnt;
}

thymeleafTutorial번역본_v1.1_20131213.pdf 파일 참고

0개의 댓글