국비/SpringBoot

2023.04.13 55일차 SpringBoot

춘핑이 2023. 4. 13. 18:22

55일차 SpringBoot

컨트롤러 메소드 작성방법을 배우고잇다.
reqeust param수집 비지니스 로직 attribute추가 foward하늗데
스프링 도움을 받아 생략이 많이된다.

ModelAttribute에 dto를 담아주었는데 생략이 가능하다.

3.21 생략

request param을 수집해서 객체의 property에 넣었다.
넣은 자바빈 객체를 model의 attribute로 넣어서 포워드햇다.
생략하지 않고 작성하면 다음과 같다.

@RequestMapping("link1")
public String method01(HttpServletRequest request, Model model) {
    //1. request param 수집가공
    String name= request.getParameter("name");
    int age = Integer.parseInt(request.getParameter("age"));

    Dto04 obj = new Dto04();
    obj.setName(name);
    obj.setAge(age);
    //2. bussiness logic

    //3. add attribute
    model.addAttribute("value", obj);

    //4. forward / redirect
    return "/sub9/link1";
}

@ModelAttribute 어노테이션을 사용하면 객체생성과 model.addAttribute를 생략할 수 있다.
또한 request parameter와 bean의 property의 이름이 같으면 생략할 수 있다.
setter를 생략하고 reqeust param을 아예 적지 않아도 된다.

// /sub9/link2?name=park&age=40
@RequestMapping("link2")
public String method02(@ModelAttribute("value") Dto04 dto04) {
    return "/sub9/link1";
}

같은 경로로 간다면 forward도 생략할 수 잇기때문에 코드가 한줄도 없어도 다음과 같은 일을 하게 된다.
따라서 코드를 작성할때 주의하면서 잘 작성할 필요가 있다.

@RequestMapping("link3")
public void method03(@ModelAttribute("attr") Dto04 dto) {
    // 1.request param수집/가공
    // Dto04 객체 만들기
    // name 요청 파라미터를 위 객체에 세팅
    // age 요청파라미터를 위 객체에 세팅
    // 3.add attribute추가
    // 위객체를 attr 이름으로 model에 추가
    // 4.forward / redirect
    // /WEB-INF/views/sub9/link3.jsp로 포워드
}

any other argument에서 아무것도 안적히면 requestparam으로 인식하고 아니라면 modelAttribute로 인지한다
그런데 simple property라면 requestparam으로 인식하는데
simple property은 String, 기본타입, 기본타입의 포장타입, Date, url등등이면 request Param으로 간주한다는 것이다.
그렇지 않으면 modelAttribute로 간주한다. 그래서 @ModelAttribute을 생략할 수도 있다.

@RequestMapping("link4")
public void method04(Dto04 dto) {
    // 1.request param수집/가공
    // Dto04 객체 만들기
    // name 요청 파라미터를 위 객체에 세팅
    // age 요청파라미터를 위 객체에 세팅
    // 3.add attribute추가
    // 위객체를 dto04 이름으로(객체 lowerCamelCase) model에 추가
    // 4.forward / redirect
    // /WEB-INF/views/sub9/link4.jsp로 포워드
}

<h1>name : ${dto04.name}</h1>
<h1>age : ${dto04.age}</h1>

3.22 연습

경로 /sub9/link5 로 쿼리스트링을 세팅하고 세팅된 프로퍼티들을 jsp에서 출력해보자.
1.쿼리스트링 작성 2.jsp작성

@RequestMapping("link5")
public void method05(Dto02 dto) {
}

<h1>sub9의 link5</h1>
<p>model : ${dto02.model}</p>
<p>price : ${dto02.price}</p>
<p>company : ${dto02.company}</p>

@RequestParam과 @ModelAttribute 의 차이?
@RequestParam은 꺼내는 것만 생략하고 setAttribute한적이 없고
@ModelAttribute 파라미터에서 꺼낸걸 property에 추가하고 setAttribute까지 한것이다.

@RequestMapping("link1")
public void method01(String name) {//@RequestParam String name
    //String name = request.getParameter("name");
}

@RequestMapping("link2")
public void method02(Dto04 dto) { //=@ModelAttribute Dto04 dto
    /*
    Dto04 dto = new Dto04();
    dto.setName(request.getParameter("name"));
    dto.setAge(Integer.parseInt(request.getParameter("age")));
    request.setAttribute("dto04", dto));
    */
}

3.23 @requestparam와 @ModelAttribute의 차이점

다음과 같을때 requestparam은 model에 추가를 안햇으니 츨력이 안된다.

// 경로 : /sub10/link3?model=ray&price=3.14&name=son&age=22
@RequestMapping("link3")
public void method03(String model, double price, Dto04 obj) {
}

<h1>sub10의 link3</h1>
<p>\${model} : ${model}</p> <!-- 출력x -->
<p>\${price} : ${price}</p> <!-- 출력x -->
<p>\${dto04.name} : ${dto04.name} </p> <!-- name -->
<p>\${dto04.age} : ${dto04.age}</p> <!-- age -->

값이 출력되게 하고 싶다면
1.EL의 param이 담긴 map을 이용
2.model.addattribute
3.@ModelAttribute 이용
@ModelAttribute을 이용하면 request에서 꺼내서 모델에 addAttribute까지 해준다.

<h1>sub10의 link3</h1>
<p>\${model} : ${model}</p>
<p>\${price} : ${price}</p>
<p>\${dto04.name} : ${dto04.name} </p> 
<p>\${dto04.age} : ${dto04.age}</p>

<hr />

<p>\${param.model} : ${param.model}</p>
<p>\${param.price} : ${param.price}</p>

@RequestMapping("link3")
public void method03(String model, double price, Dto04 obj) {
    modelMap.addAttribute("model", model);
    modelMap.addAttribute("price", price);
}

@RequestMapping("link3")
public void method03(@ModelAttribute("model") String model, @ModelAttribute("price") double price, Dto04 obj) {
}

결론은 data를 생성(create) 읽기(read) 수정(update) 삭제(delete)하는 bussiness logic에만 집중하면된다.
crud기능에 집중을 하면된다.

DB

우리가 설치한 db는 mariaDB인데 mysql만든사람이 만들어서 호환이된다.

폰트변경 edit - preference - fonts & colors
컨트롤 엔터로 sql문 실행 주석은 --

번개 모양 누르면 전체 실행된다.

w3schools의 테이블을 가져온 것이다.

MySQL의 특징
MySQL is a widely used relational database management system (RDBMS).
MySQL is free and open-source.
MySQL is ideal for both small and large applications.

관계형 db에는 mysql mariadb postrsql oracle등등이 있다.
각 db에 따라 문법이 조금씩 다르다.

데이터 읽기 수정 삭제 등등을 배울 것이다.

SQL문의 작성관습은 키워드 대문자 테이블명, 컬럼명은 소문자로 쓴다.
MariaDB는 테이블명은 대소문자 구분하고 나머지는 안한다. 회사나 버전마다 다르니 알아서 작성하기
mysql은 테이블명은 무조건 소문자 컬렴명은 무조건 대문자로 저장되고 대소문자 구분을 아예하지 않는다.
각 DB마다 다르니 기억을 잘해줘야할 듯하다.

EDIT - QUERY EDITER - Use UpperCASE~ 어쩌구하면 키워드가 대문자로 자동완성된다.

1. SELECT

데이터 읽기 또는 가져오기 이다.
데이터의 형식은 테이블 형식으로 되어있다.
그래서 데이터가 저장되어 있는 곳을 DB안의 테이블이라고 한다.
각 테이블은 각 행과 열로 이루어져있다.
각 행(row)을 레코드(record)라고 한다.
각 열(column)을 필드(field) 속성(attribute)라고 한다.

테이블의 각 레코드는 같은 attribute들을 가지고 있는 것이다. 각 레코드가 같은 필드들을 가지고 있다.
이것들을 모아둔게 테이블이다.

1.1 SELECT 문법

SELECT FROM 조합으로 데이터를 가져온다.
FROM절이 테이블명 -어디서 가져올 것인지
SELECT 뒤에는 가져올 열 어떤 열들을 가져올 것인지

Customers 테이블에서 모든 레코드(행)과 모든 필드(열)을 가져와라
SELECT * FROM Customers;

문제1) Employees 테이블에서 모든 행과 모든 열을 읽기
SELECT * FROM Employees;

1.2 특정컬럼 SELECT

SELECT뒤에 컬렴명을 적어주면된다.
여러개의 컬럼을 조회하고 싶으면 , 으로 이어주면된다.
적는 순서에 따라 나오기때문에 순서도 정할 수 있다.

-- CustomerName필드만
SELECT CUSTOMERNAME FROM Customers;
SELECT ContactName FROM Customers;
SELECT Country FROM Customers;
SELECT CustomerName, ContactName FROM Customers;
SELECT Country, CustomerName, ContactName FROM Customers;

1.3 WHERE

WHERE절은 조건 절이다.
WHERE절로 특정 레코드만 SELECT할 수 있다.
WHERE 특정열 = 값;을 해주면된다. 값은 대소문자를 구분하지 않는다.

SELECT * FROM Customers
WHERE Country = 'Mexico';

SELECT * FROM Customers
WHERE Country = 'UK';

SELECT * FROM Customers
WHERE CustomerID = 3;

문제) Country 가 USA인 공급자 이름(SupplierName)과 주소(Address)를 공급자 테이블(Suppliers)에서 조회하기

SELECT supplierName, address FROM Suppliers WHERE Country = 'USA';

1.4 WHERE 절 연산자

WHERE절에서 연산자를 사용할 수 있다. 값이 같다 크다 작다 등등이 가능하다.
버전이나 회사마다 다르니 연산자는 그때 그때 알아두자.

1.4.1 = 같다

SELECT * FROM Customers WHERE Country = 'Mexico';

1.4.2 <> != 같지않다.

SELECT * FROM Customers WHERE Country <> 'Mexico';
SELECT * FROM Customers WHERE Country != 'Mexico';

문제 나라가 USA가 아닌 공급자들 조회

SELECT * FROM Suppliers WHERE country != 'USA';

1.4.3 > 크다

수치비교도되고 문자열비교 날짜비교도 된다.

SELECT * FROM Products WHERE Price > 10.00;
SELECT productName FROM Products WHERE ProductName > 'u'; -- 문자구분없이 u 이후
SELECT * FROM Employees WHERE BirthDate > '1960-01-01';

1.4.3 < 작다

SELECT * FROM Employees WHERE BirthDate < '1960-01-01';
SELECT * FROM Products WHERE Price < 5.00;
SELECT * FROM Customers WHERE customername < 'c';

1.4.4 <= 작거나 같다 >= 크거나 같다

역시나 날짜 숫자 비교등이 가능하다.

SELECT * FROM Products
WHERE price >= 10.00;

SELECT * FROM Employees
WHERE birthdate <= '1958-09-19';

2 jdbc

이런식으로 특정 레코드들만 조회하거나 특정 열들만 조회할 수 있게 되었다.
SQL을 실행하거나 작성할 줄은 알겠는데 가져와서 화면에서 어떻게 보여주나?

클라이언트 요청 -> 서버컨트롤러 -> DB -> 서버컨트롤러 + 모델 -> VIEW -> 클라이언트
이 컨트롤러에서 DB와 연결하는게 JDBC이다.

JDBC를 체험해보자.
DB연결 - Statement 객체 생성 - 쿼리 실행 - 쿼리 실행 결과 얻고 - 쿼리실행 결과 가공으로 이루어진다.
JDBC를 마이바티스를 사용하면 거의 안쓰지만 경험을 하면 어떻게 돌아가는지 알게된다.

mariadb를 연결하는 url은 다음과 같다. jdbc:mariadb://ip번호:3306(포트번호)/DB이름

url, dbID, dbPWD의 세가지 정보가 필요한데 이 세 정보를 파일에 직접두는 건위험하니 따로 두자.
https://docs.spring.io/spring-boot/docs/current/reference/html/application-properties.html0
공식문서에서 spring boot common properties에 작성하는 내용을 보면된다

spring.datasource.url=dburl
spring.datasource.username=db아이디
spring.datasource.password=db비밀번호

그런데 여기application.properties놔둬도 같이 커밋된다.
custom.properties에 적어두고
spring.config.import=custom.properties을 작성해주면된다.

이렇게 하면 정보는 custom.properties에만 있고 깃허브에 올라가지 않는다.
그래서 보호를 할 수 있다.

이렇게 작성하면 컨트롤러에서 이 값을 가져다 사용할 수 있다.

@Value 어노테이션에 ${}로 키값들을 제공하면된다.
이러면 자동으로 주입해준다. ${} 잊지말자!!!!

@Value("${spring.datasource.url}")
private String url;
@Value("${spring.datasource.username}")
private String dbId;
@Value("${spring.datasource.password}")
private String password;

자원정리를 위해 try catch witd resources구문을 사용했다.

@Controller
@RequestMapping("sub11")
public class Controller11 {
    @Value("${spring.datasource.url}")
    private String url;
    @Value("${spring.datasource.username}")
    private String dbId;
    @Value("${spring.datasource.password}")
    private String password;

    @RequestMapping("link1")
    public void method01(Model model) {
        // 2. business logic
        // sql
        String sql = "SELECT customerName FROM Customers WHERE CustomerID = 1";

        try ( // DB연결
                Connection connection = DriverManager.getConnection(url, dbId, password);
                // Statement 객체 생성
                Statement statement = connection.createStatement();
                // 쿼리 실행
                ResultSet resultSet = statement.executeQuery(sql);) {

            // 쿼리 실행 결과 얻고 쿼리실행 결과 가공
            String name = "";
            if (resultSet.next()) {
                name += resultSet.getString("customerName");
            }
            model.addAttribute("customerName", name);

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

2.2 jdbc 연습 2

아이디 3인 직원의 last name을 조회해보자.
간단하게 콘솔에 출력하기

@RequestMapping("link2")
public void method02() {
    String sql = "SELECT lastname FROM Employees WHERE EmployeeId = 3";

    try (Connection con = DriverManager.getConnection(url, dbId, password);
            Statement st = con.createStatement();
            ResultSet rs = st.executeQuery(sql);) {

        String employeeName = "";
        if (rs.next()) {
            employeeName = rs.getString("lastname");
        }

        System.out.println(employeeName);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

연습
id가 5인 상품 이름 조회하자.

@RequestMapping("link3")
public void method03(Model model) {
    // id가 5인 상품 이름 조회
    String sql = "SELECT productname FROM Products WHERE productid = 5";
    String productName = "";
    try (Connection con = DriverManager.getConnection(url, dbId, password);
            Statement st = con.createStatement();
            ResultSet rs = st.executeQuery(sql);) {
        if (rs.next()) {
            productName = rs.getString("productname");
        }

    } catch (Exception e) {
        e.printStackTrace();
    }
    model.addAttribute("productName", productName);
}

2.3 여러 레코드 조회

업무 : 고객이름들 조회하는 jdbc를 작성해보자.

<h1>손님 목록</h1>
<ul>
    <c:forEach items="${customers}" var="customer">
        <li>${customer}</li>
    </c:forEach>
</ul>

@RequestMapping("link4")
public void method04(Model model) {
    String sql = "SELECT CustomerName FROM Customers";

    // list에 고객이름 담기
    List<String> list = new ArrayList<>();

    try {
        Connection con = DriverManager.getConnection(url, dbId, pwd);
        Statement st = con.createStatement();
        ResultSet rs = st.executeQuery(sql);

        try (con; st; rs;) {
            while (rs.next()) {
                list.add(rs.getString(1));
            }
        }

    } catch (Exception e) {
        e.printStackTrace();
    }
    model.addAttribute("customers", list);
}

여러 레코드 조회 연습
직원 이름들(firstName)들을 조회해서 jsp로 출력

@RequestMapping("link5")
public void method05(Model model) {
    // 업무 : 고객이름들 조회
    String sql = "SELECT firstName FROM Employees";

    List<String> list = new ArrayList<>();

    try {
        Connection con = DriverManager.getConnection(url, dbId, pwd);
        Statement st = con.createStatement();
        ResultSet rs = st.executeQuery(sql);

        try (con; st; rs;) {
            while (rs.next()) {
                list.add(rs.getString("firstName"));
            }
        }

    } catch (Exception e) {
        e.printStackTrace();
    }
    model.addAttribute("employees", list);
}

2023.04.13

생략이정말 많다. 편하긴하지만 익숙하지 않은 입장에선 기괴한 코드라고 밖에 생각이 안된다.
마리아 db는 테이블명을 대소문자 구분하니 주의해야할 것 같다.

Class.forName("org.mariadb.jdbc.Driver");
이건 안써도 써도 자동으로 등록된다는데 어쩔된되고 안될대고 있다고 한다. 기준을 모르겠다고 하신다.

찾아보니

JDBC API 4.0 이전 버전에서는 Class.forName() 를 사용하여 직접 사용할 JDBC Driver를 로드했어야 했다.
만약 MySQL을 사용하면 아래와 같이 코드를 작성해야했다.
Class.forName("com.mysql.cj.jdbc.Driver");
Connection connection = DriverManager.getConnection(URL, USER, PASSWORD);
JDBC API 4.0 버전부터는 DriverManager.getConnection() 메소드가 JDBC Driver를 자동 로드하도록 개선되었다고 한다.
따라서 Class.forName() 을 생략해도 괜찮다.