3월 29일

SJY0000·2022년 3월 29일
0

Springboot

목록 보기
4/24

오늘 배운 것

  • DB 쿼리문 만들어 사용하기
  • MYSQL DB에 연결하기
  • Service 클래스 만들기
  • Security 사용하기(1)

CrudRepository에 없는 객체 만들어 사용하기

  • 기본적인 CRUD 객체를 제공하지만 특별한 데이터가 필요한 경우 만들어서 사용해야함
	@Query(nativeQuery = true, value =
			"SELECT LAST_NAME AS lastName ,FIRST_NAME AS firstName, COUNT(PROJECT_ID) AS count "
			+ "FROM EMPLOYEE e LEFT JOIN PROJECT_EMPLOYEE pe  ON e.EMPLOYEE_ID = pe.EMPLOYEE_ID "
			+ "GROUP BY e.EMPLOYEE_ID "
			+ "ORDER BY count  desc ")
	public List<EmployeeProject> employeeProjects();
  • EmployeeProject 객체는 데이터만 받는 DTO 클래스

Client에서 바로 Controller에 접근 하는 것보다 데이터만 전달하는 DTO 클래스를 이용하는 것이 보안과 안정성에서 좋음

DTO클래스는 계층간 데이터 교환이 이루어 질 수 있도록 해주는 객체로 특별한 로직을 가지지 않는 순수한 데이터 객체여야만 하며 데이터를 입력하는 기능을 필요치 않음

public interface EmployeeProject {
	// Spring이 자동으로 생성하게 Interface 사용
	// set은 필요없이 DB에서 쿼리 결과를 가져오기만 하면 됨
	public String getFirstName();
	public String getLastName();
	public String getCount(); 
}

home.html, HomeController에도 수정

            <tbody>
              <tr th:each="employee : ${empProList}">
                <td th:text="${employee.lastName}"></td>
                <td th:text="${employee.firstName}"></td>
                <td th:text="${employee.count}"></td>
              </tr>
            </tbody>
	@GetMapping("/")
	public String displayHome1(Model model) {
		List<Project> projectList = projectService.findAll();
		List<EmployeeProject> empProList = employeeService.employeeProjects();
		
		model.addAttribute("projectList", projectList);
		model.addAttribute("empProList", empProList);
		return "main/home";
	}


MYSQL DB에 연결하기

  • POM.XML에서 오른쪽 클릭 -> Spring -> Add Starters -> MySQL Driver 선택

  • application.properties에 아래의 내용을 입력

# MySQL DB 설정
spring.datasource.url=jdbc:mysql://localhost:3306/pma?useSSL=false&serverTimezone=Asia/Seoul
spring.datasource.username=MySQL 아이디
spring.datasource.password=MySQL 비밀번호
  • DB연결 최초 실행시 아래의 내용을 입력하여 테이블을 자동으로 생성
# 초기 테스트용(자동으로 테이블 생성 및 수정, 사제) 1회만 사용
spring.jpa.hibernate.ddl-auto=update

Service 클래스 만들기

서버에서 처리과정을 크게 3가지로 분리 ( Controller, Service, Repository)

Controller

Client의 요청을 받음
요청에 대한 처리는 Service에게 전담
Client에게 응답

Service

사용자의 요구사항 처리
DB정보가 필요할 때는 Repository에게 전달

Repository

DB관리 (연결, 해제, 자원관리)
DB CRUD 작업처리

Controller 와 Service를 구분하는 이유

구분되지 않고 하나의 클래스에서 사용하면 Controller마다 같은 메소드를 구현해야 하므로 중복코드가 발생하고 재사용성이 떨어진다
확장성과 재사용성이 좋아지고 중복코드를 제거 할 수 있다는 점에서 분리하여 사용해야함

EmployeeService 클래스

package com.myapp.pma.services;

import java.util.List;

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

import com.myapp.pma.dao.EmployeeRepository;
import com.myapp.pma.dto.EmployeeProject;
import com.myapp.pma.entities.Employee;

@Service
public class EmployeeService {

	@Autowired
	private EmployeeRepository employeeRepository;
	
	public List<Employee> findAll() {
		return employeeRepository.findAll();
	}
	
	public void save(Employee employee) {
		employeeRepository.save(employee);
	}

	public List<EmployeeProject> employeeProjects() {
		return employeeRepository.employeeProjects();
	}
}

기존의 EmployeeRepository에 바로 접근하여 사용하던 곳에 EmployeeService 클래스로 대체

package com.myapp.pma.controllers;

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.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import com.myapp.pma.entities.Employee;
import com.myapp.pma.services.EmployeeService;

@Controller
@RequestMapping("/employees")
public class EmployeeController {

	@Autowired
	private EmployeeService employeeService;
	
	@GetMapping("/new")
	public String displayPrjectForm(Model model) {
		Employee e = new Employee();
		
		model.addAttribute("employee", e);
		return "employees/new-employee";
	}
	
	@PostMapping("/save")
	public String createProject(Employee employee) {
		employeeService.save(employee); // DB에 employee객체를 테이블에 저장
		return "redirect:/employees/new"; //post-redirect-get 패턴
	}
	
	@GetMapping("/")
	public String displayEmployees(Model model) {
		List<Employee> employeeList = employeeService.findAll();
		model.addAttribute("employeeList", employeeList);
		
		return"employees/list-employees";
	}
}

ProjectService 클래스

package com.myapp.pma.services;

import java.util.List;

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

import com.myapp.pma.dao.ProjectRepository;
import com.myapp.pma.entities.Project;

@Service
public class ProjectService {

	@Autowired
	private ProjectRepository projectRepository;
	
	public List<Project> findAll() {
		return projectRepository.findAll();
	}
	
	public void save(Project project) {
		projectRepository.save(project);
	}
}

기존의 ProjectRepository에 바로 접근하여 사용하던 곳에 ProjectService 클래스로 대체

package com.myapp.pma.controllers;

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.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import com.myapp.pma.entities.Employee;
import com.myapp.pma.entities.Project;
import com.myapp.pma.services.EmployeeService;
import com.myapp.pma.services.ProjectService;

@Controller
@RequestMapping("/projects")
public class ProjectController {
	// Spring에서 repository객체를 처음에 자동생성해서 가지고 있다가
	// Autowired는 관련 객체를 필요할 때 자동으로 연결해준다.
	@Autowired
	private ProjectService projectService;

	@Autowired
	private EmployeeService employeeService;
	
	@GetMapping("/new")
	public String displayPrjectForm(Model model) {
		Project p = new Project();
		
		model.addAttribute("project", p);
		
		List<Employee> empList = employeeService.findAll();
		
		model.addAttribute("empList", empList);
		
		return "projects/new-project";
	}
	
	// 프로젝트 생성할 때 직원 선택을하면 "employees"라는 이름의 List<Employee> 타입의 데이터를 변수로 받음
	@PostMapping("/save")
	public String createProject(Project project, @RequestParam("employees") List<Long> ids) {
		projectService.save(project); // DB에 project객체를 테이블에 저장
//		
//		// Project 생성 html에서 올라온 직원 id 값들을 입력하여 직원리스트를 가져와 각각의 직원에ㅔ게 프로젝트를 입력
//		Iterable<Employee> selectEmployees =  employeeRepository.findAllById(ids);
//		for(Employee emp : selectEmployees) {
//			emp.setProject(project); // 각각의 직원 객체에 프로젝트 입력하고
//			employeeRepository.save(emp); // DB에 다시 저장
//		}
		return "redirect:/projects/new"; //post-redirect-get 패턴
	}
	
	@GetMapping("/")
	public String displayProjects(Model model) {
		List<Project> projectList = projectService.findAll();
		
		model.addAttribute(projectList);
		
		return"projects/list-projects";
	}
}

Security 사용하기

POM.XML에서 오른쪽 클릭 -> Spring -> Add Starters -> Spring Security 선택

설정 후 페이지 접근 시 로그인 페이지로 이동함

기본 아이디 : user
비밀번호 : Spring 실행 시 Console 창에 출력됨

Security 설정하기

  • configure(AuthenticationManagerBuilder auth)에서 새로운 아이디와 비밀번호, 권한을 입력
  • configure(HttpSecurity http)에서는 권한별 기능을 설정
  • Security 사용 시 csrf 방지가 기본적으로 적용중이기 때문에 데이터를 전송하거나 페이지를 이동할 때 확인이 필요함

csrf 란?

사용자의 의지와 무관하게 행동하는 것을 의미하며 이를 방지하기 위해서 get방식 보다는 Post방식을 이용하고 csrf방지 기능이 있는 라이브러리 등을 사용한다.
(thymeleaf도 csrf방지 기능이 있음)

package com.myapp.pma.security;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@EnableWebSecurity
@Configuration
// Security 설정을 위해서 WebSecurityConfigurerAdapter 상속 받음
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
	
	@Override // 인증
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		// 메모리에 아이디, 비밀번호, 역할(권한)설정
		auth.inMemoryAuthentication()
			.withUser("admin").password(getPasswordEncoder().encode("1234")).roles("ADMIN")
			.and()
			.withUser("asd").password(getPasswordEncoder().encode("asd")).roles("USER");
		
	}
	@Bean // 패스워드 암호화 객체
	public PasswordEncoder getPasswordEncoder() {
		return NoOpPasswordEncoder.getInstance(); // 테스트용 비밀번호 암호화(실제론 안됨)
	}
	
	@Override // 허가
	protected void configure(HttpSecurity http) throws Exception {
		http.authorizeHttpRequests()
			.antMatchers("/projects/new").hasRole("ADMIN") // 새 프로젝트는 관리자만
			.antMatchers("/projects/save").hasRole("ADMIN")
			.antMatchers("/employees/new").hasRole("ADMIN") // 새 직원은 관리자만
			.antMatchers("/employees/save").hasRole("ADMIN")
			.antMatchers("/employees/").authenticated() // 인증된 유저
			.antMatchers("/projects/").authenticated()
			.antMatchers("/").permitAll()				// 누구든 접근 가능
			.and()
			.formLogin();									// 로그인창 사용
		
		// Security에서는 기본적으로 csrf 방지가 적용중
//		http.csrf().disable(); // 사용자 의도치 않게 행동하는 것을 방지, save 후 redirect 하는 과정에서 csrf 룰에 위배되어 에러 출력
	}
}

0개의 댓글