스프링 부트를 이용한 유저리스트 화면에 뿌리기
728x90

요즘 vue.js만 공부하다 보니 스프링 부트 쪽 공부가 잘 안 되는 거 같아서 스프링 부트로 유저 리스트를 구현해 봤다. css는 신경 쓰지 않고 기능만 구현해봤다.

 

준비물 : IntelliJ , 오라클 db

 

먼저 스프링 부트 프로젝트를 만든다. 

빌드 도구는 gradle을 사용하였고, 자바 버전은 11 버전으로 하였다. name 부분에 자기가 하고 싶은 프로젝트 명을 적고 next 클릭

라이브러리는 기본적으로 이렇게 가져왔다. 이제 finish를 클릭하면 프로젝트 생성이 끝난다.

 

프로젝트 생성이 끝나면 이제 패키지를 작성하는데 이렇게 만든다.

이제 하나 씩 설명이라기 보다 내 생각을 적겠다.

 

controller는 처음 클라이언트가 url에 접속을 하면 컨트롤러가 받아서 보여줄 화면을 리턴한다.

 

domain은 DTO객체가 있다.

 

repository는 DAO객체가 있다.

 

Service는 비즈니스 로직(즉 모든 로직)이 있다.

 

controller에서 하면 좋지 않겠냐는 생각도 들겠지만

웬만한 로직들은 service에서 해결하도록 하자...

 

그리고 하나의 기능당 controller, service,dto,dao를 만들어서 하자....

 

 

 

만들었으면 이제 먼저 해야할 것은 application.properties로 가서 db등록을 해준다..

그리고 마이바티스의 mapper-lcoation을 등록한다. 그리고 밑에는 잘렸지만 타임리프를 등록할 때는 이렇게 적도록 한다

mybatis.mapper-locations=classpath:Mappers/*. xml이란 클래스 패스(src/main/resources) 경로에 있는 Mapper폴더 안에 있는 모든 xml파일을 인식한다는 뜻이다. 그래서 Mappers폴더 안에 xml로 sql문을 저장하면 된다. 무슨 이야기인지 모르겠으면 일단 해보고 분석하길 바란다.

 

그리고 타임리프 쪽인데 밑에가 잘려서 추가적으로 더 넣어준다.

spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
spring.thymeleaf.cache=false
spring.thymeleaf.enabled=true

prefix는 기본이 저렇게 인데 그냥 적어봤다.

 

suffix는 사용할 파일 형식?이다. html을 쓸 거면 html로 jsp이면 jsp로 바꿔준다.

 

cache는 false로 해놓았을 때 타임리프를 수정하고 서버를 재시작할 필요 없이 새로 고침만으로 반영이 된다.

(서버 재시작을 안 해도 되어 매우 편리하다!)

개발할 때는 false로 두고 반영 시에는 true로 설정해놓는 게 좋다.

 

enalbled 타임리프를 사용하겠다는 뜻이다.

당연히 true로 설정해둔다.

 

이제 다 설정이 끝났다면 Controller로 넘어가자

먼저 컨트롤러는 이렇게 만든다. @GetMapping("/")는 기본적으로 localhost:8080으로 들어오는 모든 접속이다.

모든 접속에 대해 main 화면을 보여주겠다는 뜻이다.

 

MVC는 spring에서 중요하다 잘 기억하도록 하자...

return "main";은 번호로 치면 8번 이다. 위는 MVC 구조인데 나도 아직 잘 이해를 못한다.. 

이렇게 했으면 이제 유저 리스트를 뽑기위한 데이터베이스 작업이 필요하다. 일단 oracle에서 db에 더미 값을 넣어준다.(넣는 건 각자 알아서....)

 

그 후에 인텔리제이에서 db를 등록한다.

화면 제일 오른쪽에 Database클릭후 현재 사진에 보이듯이 +클릭 후에 Data Source클릭 후 Oracle(사용하는 db아무거나) 클릭한다.

대충 보이듯이 User: 와 Password: 부분에 뭘 넣어야 할지 알 것이다. 아까 application.properties에서 넣은 

db 이름과 비밀번호를 적는다. 그 후에 ok를 누르면 연동이 끝난다.

요렇게 잘 들어가 있다..

 

 

 

 

 

 

 

 

 

 

 

이제 다음으로 해야 할 것은 domain에 있는 UserDTO를 수정해야 한다.

package com.example.te.domain;

import lombok.Data;

@Data
public class UserDTO {
    private String user_name;
    private String user_email;
    private String user_pw;
    private String birth_date;
    private String user_gender;
    private String user_type;
    private String user_nickname;

DTO 클래스는 이렇게 @Data로 자동으로 getter , setter를 생성해준다. 우리가 아까 넣은 라이브러리 중에 Lombok이 

getter와 setter를 자동으로 만들어준다. 저 어노테이션만 쓰면.. 그런데 Entity처럼 getter만 있어야 하는 경우는 

그냥 @getter만 하면 된다. setter도 마찬가지...

그리고 DTO에 있는 변수들은 db의 테이블에 있는 칼럼 명과 똑같아야 한다. 대. 소문자는 상관없지만 칼럼에서 

user_name인 것을 username 이렇게 하면 인식을 못한다...

 

이제 이렇게 했으면 Mappers 폴더에 user-mapper.xml 파일을 만들어준다.

이제 이 xml파일은 sql문을 저장하는 용도로 쓰일 것이다. 이름이 왜 user-mapper냐면 user관련 정보에 쓰일 매퍼이기 때문이다. 기능별로 repository라든지 controller라든지 나누는 게 좋다. 이유는 모르지만 만들다 보면 편하기 때문이다.

 

이제 user-mapper.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.example.te.repository.UserDAO">
    <select id="selectUser" resultType="com.example.te.domain.UserDTO">
        SELECT * FROM USERDATA
    </select>
</mapper>

mapper의 namespace는 repository에 있는 DAO 인터페이스로 한다. DAO인터페이스에 추상 메서드를 만든 후에 CRUD문에서 id값을 DAO의 추상 메서드의 이름으로 하면 sql문 실행 후 반환을 해준다. 이렇게 작성해주었으면 repository에 있는 UserDAO로 가보자.

 

UserDAO 클래스는 이렇게 작성해준다.

package com.example.te.repository;

import com.example.te.domain.UserDTO;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
@Mapper
public interface UserDAO {
    List<UserDTO> selectUser();
}

@repository 빈 등록을 하면서 @repository 밑에 @Mapper라고 돼있는 부분이 있다. 이 부분이 xml파일과 연결시켜주는 역할을 한다. 안 하면 오류가 나기 때문에 연결해주는 역할이라고 생각했다...

이제 저렇게 메서드 명을 정하고 반환형을 List <UserDTO>로 만든 후에 다시 xml로 가서 <select> 부분을 보자

    <select id="selectUser" resultType="com.example.te.domain.UserDTO">
        SELECT * FROM USERDATA
    </select>

id 값과 DAO에 있는 메서드 명이 같다. 이렇게 하면 이제 resultType으로는 UserDTO로 반환한다고 적어 놓으면 DAO에 있는 메서드의 반환 형으로 UserDTO가 계속 들어간다. 반환 타입이 List인데 왜 resultType이 DTO객체냐고 생각이 날 수 있는데 그건 이제 spring이 알아서 해줄 것이다...

 

이제 service로 넘어가 본다...

 

service에는 itf부분에 서비스 인터페이스가 있는 것을 볼 수 있다. 먼저 interface 부분에는 추상 메서드를 만들어주고 뭐.. 인터페이스를 왜 만드느냐는 제어의 역전이라는 말이 어렴풋이 떠오르는데 이것에 대한 궁금증은 

https://codevang.tistory.com/312

 

스프링 의존 주입(DI)과 인터페이스 사용에 관하여

인터넷에서 자료를 검색하다보면 클래스를 작성할 때 무조건적으로 인터페이스 구현을 하고 있는 것을 심심찮게 볼 수 있습니다. 예를 들어 인터페이스 이름이 "user"라면 실제 구현되는 서비스

codevang.tistory.com

여기에서 알아보도록 하자....

 

쉽게 말해서 클래스들끼리의 결합을 떨어 뜨려 놓기 위해서?라고 생각하면 되겠다. 스프링이 부품들이 모여있는 형태라고 보면 이해가 갈 수 도 있다. 자동차가 있다고 생각해보자.... 자동차의 부품들이 다 연결되어 있다고 생각해보면 아주 사소한 부품이 고장났는데 자동차를 바꿔야될 경우가 있다. 그래서 이 처럼 클래스들 간의 결합을 약하게 하는 것을 인터페이스로 나누어 준다고 생각하면 될거 같다. 이 설명은 좀 구리므로 위의 사이트에 들어가서 개념을 익히도록 하자.

 

이제 serviceImpl 클래스로 와서 구현해준다. 당연히 service 인터페이스에서는 이렇게 만들어 준다.

package com.example.te.service.itf;

import com.example.te.domain.UserDTO;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;

public interface UserService {
    List<UserDTO> selectUser();
    }

그리고 이 인터페이스의 구현체는 이렇게

package com.example.te.service;

import com.example.te.domain.UserDTO;
import com.example.te.repository.UserDAO;
import com.example.te.service.itf.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.PrintWriter;
import java.util.List;
@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private final UserDAO userDAO;

    public UserServiceImpl(UserDAO userDAO) {
        this.userDAO = userDAO;
    }

    @Override
    public List<UserDTO> selectUser() {
        return userDAO.selectUser();
    }
}

@Service라는 어노테이션을 붙여서 빈을 주입한다...

@Autowired로 UserDAO 인터페이스에 빈을 주입하고 연결을 시킨다. 이렇게 하면 service와 repository가 연결되었다..

이제 userServiceImpl.selectUser()를하면 userDAO.selectUser()의 리턴 값이 온다...

 

이제 이 return 값을 들고 controller로 가자...

package com.example.te.controller;

import com.example.te.domain.UserDTO;
import com.example.te.service.UserServiceImpl;
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.PostMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;

@Controller
public class UserController {
    @Autowired
    private final UserServiceImpl userService;

    public UserController(UserServiceImpl userService) {
        this.userService = userService;
    }

    @GetMapping("/")
    public String main(){return "main";}
    @GetMapping("/user_list")
    public String user_list(Model model){
        List<UserDTO> selectUser = userService.selectUser();
        model.addAttribute("list",selectUser);
        return "user_list";
    }
}

컨트롤러는 이렇게 만들어 놓는다... 이제 구성은 main.html에서 유저리스트라는 버튼을 누르면 location.href="/user_list" 로 해놓으면 이제 @GetMapping("/user_list)가 받고 user_list 메서드를 실행시킨다..

이제 user_list는 userService.selectUser()메서드를 실행시키고 리턴된 list의 값을 model에 담아서 화면으로 던진다..

이런 흐름이다.

 

이제 html 화면 부분의 코드를 적어본다...

 

각각의 코드는 이러하다..

main.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
  <button onclick="location.href='/login'">로그인</button>
  <button onclick="location.href='/signup'">회원가입</button>
  <button onclick="location.href='/user_list'">회원리스트</button>
</body>
</html>

버튼에 온클릭으로 각각의 url로 접근하게 만들었다. 

 

user_list.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<table>
  <thead>
    <tr>
      <td>이름</td>
      <td>이메일</td>
      <td>비밀번호</td>
      <td>생일</td>
      <td>성별</td>
      <td>타입</td>
      <td>닉네임</td>
    </tr>
  </thead>
  <tbody>
    <tr th:each="lists:${list}">
      <td th:text="${lists.user_name}"></td>
      <td th:text="${lists.user_email}"></td>
      <td th:text="${lists.user_pw}"></td>
      <td th:text="${lists.birth_date}"></td>
      <td th:text="${lists.user_gender}"></td>
      <td th:text="${lists.user_type}"></td>
      <td th:text="${lists.user_nickname}"></td>
    </tr>
  </tbody>
</table>
</body>
</html>

이제 thymeleaf로 하나씩 list에 있는걸 뽑아서 화면에 뿌린다... th:each="lists:${list}"는 아까 모델객체에 담아 보낸 "list"가 ${list}이다. 이제 for each처럼 생각하면 편하다... for( UserDTO lists : list){}이런 느낌이랄까...

 

이렇게 하고 실행시켜서 해당 url로 가보면 

잘 실행된다... 이제 회원리스트를 클릭해보면 url이 바뀌면서 화면이 바뀐다..

/user_list를 컨트롤에서 받고 서비스에서 로직처리를하고 dao에서 데이터베이스에 접근해서 값을 가져온 후에 리턴을 해 컨트롤러로 다시 오는 순서이다. 컨트롤러에서는 모델 객체에 담아서 화면으로 뿌려주고 그걸 타임리프가 해준다.

 

이런 순서라고 생각하면 쉬울거 같다.

 

나만 알자고 설명을 이상하게 해놨지만 잘 읽다보면 해답이 보일지도 모르겠다. 

 

아 그리고 오라클 db 쓰는 사람들은 application.properties에서 서버 포트 바꾸고 가길 바란다. 8080은 오라클이 쓰고 있기 때문에 application.properties에서 server.port=하고싶은 포트번호 를 적으면 끝

728x90

'이것저것 > 스프링' 카테고리의 다른 글

좋은 객체 지향 설계의 5가지 SOLID  (0) 2022.11.15
IOC  (0) 2021.07.21