일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
- springaop
- javascript
- TodayILearned
- 자바스크립트
- 프로그레시브웹앱
- Oracle
- HTML
- 스프링
- SpringMVC
- js
- 프레임워크
- sql
- javaprogramming
- 서브쿼리
- JavaScript 내장객체
- TIL
- web
- maven
- tdd
- 국비지원
- 오라클
- CSS
- progressive web app
- framework
- 생활코딩
- 메이븐
- sqldeveloper
- mybatis
- 자바프로그래밍
- PWA
- Today
- Total
1cm
자바 프로그래밍_Day_109_회원정보 관련 본문
2022. 01. 19
- 회원가입 (복습 참고)
<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
<!--
mapping 속성에 해당하는 요청에 대해 location 속성에 지정 된 디렉터리(폴더)로 매핑을 시켜준다.
-->
<resources mapping="/resources/**" location="/resources/" />
<!--
사용자로 부터 /js/jquery-3.6.0.js 라는 요청이 오면 이것을 /resources/js/jquery-3.6.0.js로 매핑시킨다.
-->
<resources mapping="/js/**" location="/resources/js/" />
- servlet-context.xml
resource로 시작하는 요청이나 js로 시작하는 요청은 Dispatcher Servlet에서 직접 처리하는 것이 아닌 location 경로와 매핑하여 바로 내려줄 수 있게끔 설정해줄 수 있는 속성이다.
- 회원가입 로직 구현
@PostMapping("/member/enroll")
public String enroll(@ModelAttribute Member member) {
log.info(member.toString());
int result = service.save(member);
return "member/enroll";
}
int -> 영향받은 행의 개수(정수형)
MemberService.java -> 추상 메소드 추가
package com.kh.mvc.member.model.service;
import com.kh.mvc.member.model.vo.Member;
public interface MemberService {
Member login(String id, String password);
int save(Member member);
}
MemberServiceImpl.java -> 메소드 추가
@Override
public int save(Member member) {
int result = 0;
if(member.getNo() != 0) {
// update
} else {
// insert
result = mapper.insertMember(member);
}
return result;
}
MemberMapper.java
@Mapper
public interface MemberMapper {
// 아래 메소드를 호출 시 실제 mapper.xml에 있는 쿼리문을 수행한 다음 결과를 리턴해주는 인터페이스의 구현체가 만들어진다.
Member findMemberById(@Param("id") String id);
int insertMember(Member member);
- 트랜젝션 처리
기존 : result값을 보고 commit, rollback 진행 -> 예외 발생시 실제 앞에서 작업했던 것이 rollback이 되어야 함.
@Override
public int save(Member member) {
int result = 0;
if(member.getNo() != 0) {
// update
} else {
// insert
result = mapper.insertMember(member);
}
// if(true) {
// throw new RuntimeException();
// }
return result;
}
mybatis-context.xml
- 트랜젝션 매니저 또한 빈으로 등록하여 사용함
<!--
트랜잭션 처리 방법
1. 트랜젝션 매니저 등록하기
2. @Transactional을 사용해서 트랜젝션 처리가 될 수 있도록 <tx:annotation-driven />태그를 설정 파일(servlet-context.xml)에 등록한다.
-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
p:dataSource-ref="dataSource"
/>
servlet-context.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:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.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
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
트랜젝션 처리를 위한 태그네임스페이스인 xmlns:tx와 스키마 정보 추가,
어노테이션을 사용해서 트랜젝션을 처리하겠다는 설정을 위한 tx:annotation-driven 추가
<!--
DB 관련 설정은 root-context.xml에서 import하는 mybatis-context.xml에 작성했지만
<tx:annotation-driven />는 실제 트랜젝션을 적용할 빈들이 등록되는 ApplicationContext 설정 파일에 작성해야 한다.
-->
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>
MemberServiceImpl.java 에서 @Transactional을 붙여준 뒤 테스트 진행하게 되면
@Override
@Transactional
public int save(Member member) {
int result = 0;
if(member.getNo() != 0) {
// update
} else {
// insert
result = mapper.insertMember(member);
}
if(true) {
throw new RuntimeException();
}
return result;
}
예외 발생 시 rollback된다 -> 데이터 조회 X
- aop로 활용한 것
만약 어노테이션 기반으로 사용 안할 시 메소드 하나하나씩 설정을 해줘야 한다.
class에 붙여서 사용도 가능한데, 그렇게 되면 전체 적용이 되지만 필요한 메소드에만 붙여줘서 트랜젝션 처리가 될 수 있게 해주는 것이 더 좋다.
마찬가지로 Service의 인터페이스에도 붙여서 사용도 가능하다.
- Controller
@PostMapping("/member/enroll")
public ModelAndView enroll(ModelAndView model, @ModelAttribute Member member) {
log.info(member.toString());
int result = service.save(member);
if(result > 0) {
model.addObject("msg", "회원가입이 정상적으로 완료되었습니다.");
model.addObject("location", "/");
} else {
model.addObject("msg", "회원가입을 실패하였습니다.");
model.addObject("location", "/member/enroll");
}
model.setViewName("common/msg");
return model;
}
- 패스워드 암호화
MVN Repository - Spring Security Core, Web, Config 라이브러리들을 pom.xml에 추가
dependency추가 후 버전을 ${}로 관리해준다.
<!-- Spring-security -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>${org.springsecurity-version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>${org.springsecurity-version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>${org.springsecurity-version}</version>
</dependency>
암호화는 Application Context에서 bean으로 만들어서 활용
security-context.xml 생성
<?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:c="http://www.springframework.org/schema/c"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 암호화를 위한 BCryptPasswordEncoder 등록 -->
<bean id="bcryptPasswordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>
</beans>
BCrypt(비크립트) -> 암호화 방식
-> password 생성 시 랜덤한 솔트를 침(1234를 암호화 시 매번 다르게 생성(솔트:salt 를 친다))
키스트레칭 -> 암호화 과정을 여러번 거치는 기법 (암호화된 값을 암호화 -> 또 암호화)
> root-context.xml에 security-context.xml를 import 시켜준다.
<import resource="security-context.xml"/>
> MemberServiceImpl.java
@Autowired
private BCryptPasswordEncoder passwordEncoder;
save하기 전에 패스워드를 암호화 시켜준다.
@Override
@Transactional
public int save(Member member) {
int result = 0;
if(member.getNo() != 0) {
// update
} else {
// 패스워드 암호화
member.setPassword(passwordEncoder.encode(member.getPassword()));
// insert
result = mapper.insertMember(member);
}
// if(true) {
// throw new RuntimeException();
// }
return result;
}
그 뒤 로그인에서 syso로 확인해보면 (System.out.println())
@Override
public Member login(String id, String password) {
Member member = null;
// member = dao.findMemberById(session, id);
member = mapper.findMemberById(id);
// System.out.println(mapper.findAll());
System.out.println(passwordEncoder.encode(password));
System.out.println(member.getPassword());
System.out.println(passwordEncoder.matches(password, member.getPassword()));
// if (member != null && member.getPassword().equals(passwordEncoder.encode(password))) {
// return member;
// } else {
// return null;
// }
}
원문은 전부 1234이지만
암호화된 결과는 다 다르게 뜬다. -> 기존 if 방식 사용하기 어려움
-> matches에 기존 암호, 암호화된 password를 넘겼을 때 결과값이 true가 반환된다.(복호화 가능) / 값이 다를경우 false 반환
@Override
public Member login(String id, String password) {
Member member = null;
// member = dao.findMemberById(session, id);
member = mapper.findMemberById(id);
// System.out.println(mapper.findAll());
// System.out.println(passwordEncoder.encode(password));
// System.out.println(member.getPassword());
// System.out.println(passwordEncoder.matches(password, member.getPassword()));
// if (member != null && member.getPassword().equals(passwordEncoder.encode(password))) {
// return member;
// } else {
// return null;
// }
return member != null &&
passwordEncoder.matches(password, member.getPassword()) ? member : null;
}
-> 삼항 연산자로 작성
* log4j.xml
- priority value = debug로 변경
<!-- Root Logger -->
<root>
<priority value="debug" />
<appender-ref ref="console" />
</root>
- 중복검사
(json 형태로 데이터 내려받기)
enroll.jsp에서 if문 잠시 주석 처리
> MemberController.java
@PostMapping("/member/idCheck")
public String idCheck(@RequestParam("userId") String userId) {
log.info("{}", userId);
return "안녕하세요 " + userId + "님";
}
일단 문자열로 데이터를 넘겨주기
-> 404에러 : Controller에서 리턴해주는 것이 문자열을 리턴해주기 때문 : 문자열:view이름 - Dispatcher Servlet이 view이름을 찾으려고 하는데 존재하지 않기 때문
@GetMapping("/member/idCheck")
@ResponseBody
public String idCheck(@RequestParam("userId") String userId) {
log.info("{}", userId);
return "Hello " + userId;
}
-> 반환하는 리턴값이 view 이름이 아닌 응답하는 body쪽에 데이터를 바로 담아서 출력해주기 위해 @ResponseBody를 붙여준다.
: 데이터(문자열)를 응답의 body에 담아서 리턴해줌
* 한글이 깨져서 영어로 바꿔줌
json 테스트를 할 수 있는 컨트롤러 생성
// json 테스트할 수 있는 컨트롤러 생성
@GetMapping("/member/jsonTest")
@ResponseBody
public String jsonTest() {
return "Hello";
}
/*
* @ResponseBody
* - 일반적으로 컨트롤러 메소드의 반환형이 String 타입일 경우 뷰의 이름을 찾아서 반환한다.
* - @ResponseBody 붙은 String 반환은 해당 요청을 보낸 클라이언트에 전달할 데이터를 의미한다.
*
* jackson 라이브러리
* - 자바 객체를 JSON 형태의 데이터로 변환하기 위한 라이브러리이다.(GSON, jsonSimple)
* - 스프링에서는 jackson 라이브러리를 추가하고 @ResponseBody을 사용하면 리턴되는 객체를 자동으로 JSON으로 변경해서 응답한다.
*
* @RestController
* - 해당 컨트롤러의 모든 메소드에서 데이터를 반환하는 경우 사용한다.
* - @Controller와 @ResponseBody를 합쳐놓은 역할을 수행한다.
*/
@GetMapping("/member/jsonTest")
@ResponseBody
public Object jsonTest() {
return new Member("mrhong", "1234", "홍길동");
}
객체 형태 -> 에러 발생
@GetMapping("/member/jsonTest")
@ResponseBody
public Object jsonTest() {
Map<String, String> map = new HashMap<>();
map.put("hi", "hello");
// return new Member("mrhong", "1234", "홍길동");
return map;
}
map 형태도 마찬가지로 에러가 발생한다. - 406에러 발생
mvnrepository에서 jackson databind -> pom.xml에 추가시켜줌
<!-- jackson -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.1</version>
</dependency>
json 관련된 라이브러리이다.
-> 자바 객체를 json 문자열 형태로 변환해주는 라이브러리. (gson과 비슷)
// json 테스트할 수 있는 컨트롤러 생성
@GetMapping("/member/jsonTest")
@ResponseBody
public Object jsonTest() {
// Map<String, String> map = new HashMap<>();
//
// map.put("hi", "hello");
//
// return map;
return new Member("mrhong", "1234", "홍길동");
}
그리고 재실행시 json 문자열 형태로 잘 넘어오는 것을 확인할 수 있다.
@RestController -> 모든 메소드에서 데이터로 반환하는 경우 최상단에 달아준다.
- idCheck 구현
// 아이디 중복체크
@PostMapping("/member/idCheck")
@ResponseBody
public String idCheck(@RequestParam("userId") String userId) {
log.info("{}", userId);
return "Hello " + userId;
}
->
@PostMapping("/member/idCheck")
@ResponseBody
public Object idCheck(@RequestParam("userId") String userId) {
Map<String, Boolean> map = new HashMap<>();
log.info("{}", userId);
map.put("duplicate", false);
return map;
}
->
public Object idCheck(@RequestParam("userId") String userId) {
Map<String, Boolean> map = new HashMap<>();
log.info("{}", userId);
map.put("duplicate", service.isDuplicateID(userId));
return map;
}
isDuplicaateId 메소드가 없어서 발생하는 오류 해결을 위해 메소드들 생성
> MemberService.java
//@Transactional
public interface MemberService {
Member login(String id, String password);
int save(Member member);
Boolean isDuplicateID(String userId);
}
> MemberServiceImpl.java
@Override
public Boolean isDuplicateID(String id) {
return mapper.findMemberById(id) != null;
}
enroll의 주석 해줬던 if부분을 풀어준다.
> MemberController.java
// 아이디 중복체크
@PostMapping("/member/idCheck")
// @ResponseBody
// @ResponseBody를 사용하지 않고 ResponseEntity를 사용하는 방법
public ResponseEntity<Map<String, Boolean>> idCheck(@RequestParam("userId") String userId) {
Map<String, Boolean> map = new HashMap<>();
log.info("{}", userId);
map.put("duplicate", service.isDuplicateID(userId));
return new ResponseEntity<Map<String,Boolean>>(map, HttpStatus.OK);
}
pom.xml에 테스트코드 추가
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>2.6.0</version>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-all</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>
MemberControllerTest.java 생성
> MemberControllerTest.java
package com.kh.mvc.member.controller;
import static org.hamcrest.CoreMatchers.is;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.content;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
@ExtendWith(SpringExtension.class)
@ContextConfiguration(
locations = {
"file:src/main/webapp/WEB-INF/spring/appServlet/servlet-context.xml",
"file:src/main/webapp/WEB-INF/spring/root-context.xml"
}
)
class MemberControllerTest {
@Autowired
MemberController controller;
MockMvc mockMvc;
@Test
public void create() throws Exception {
mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
mockMvc.perform(get("/member/enroll"))
.andExpect(view().name("member/enroll"))
.andExpect(status().isOk());
}
@Test
public void idCheckTest() throws Exception {
mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
mockMvc.perform(post("/member/idCheck").param("userId", "ismoon"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.duplicate", is(true)));
}
}
전체 실습 코드
> MemberController.java
package com.kh.mvc.member.controller;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.bind.support.SessionStatus;
import org.springframework.web.servlet.ModelAndView;
import com.kh.mvc.member.model.service.MemberService;
import com.kh.mvc.member.model.service.MemberServiceImpl;
import com.kh.mvc.member.model.vo.Member;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Controller
//@RestController
//Model 객체에 loginMember라는 키값으로 객체가 추가되면 해당 객체를 세션 스코프에 추가하는 어노테이션이다.
@SessionAttributes("loginMember")
public class MemberController {
/*
// controller가 처리하게 될 요청에 대해 정의함(URL, Method 등)
// @RequestMapping(value = "/login", method = {RequestMethod.GET})
@GetMapping("/login")
// 요청을 처리할 메소드 작성
public String login() {
log.info("login() - 호출");
return "home";
}
사용자의 파라미터를 전송받는 방법
1. HttpServletRequest를 통해서 전달(전송)받기(기존 JSP/Servlet 방식)
- 메소드의 매개 변수로 HttpServletRequest를 작성하면 메소드 실행 시 스프링 컨테이너가 자동으로 객체를 인자로 주입해준다.
@PostMapping("/login")
public String login(HttpServletRequest request) {
String id = request.getParameter("id");
String password = request.getParameter("password");
log.info("login() - 호출 : {} {}", id, password);
return "home";
}
2-1. @RequestParam 어노테이션을 통해서 전송받기
- 스프링에서 조금 더 간편하게 파라미터를 받아올 수 있는 방법 중 하나이다.
- 내부적으로는 Request 객체를 이용해서 데이터를 전송받는 방법이다.
- 단, 매개변수의 이름과 name 속성의 값이 동일하게 설정된 경우 자동으로 주입된다.
(어노테이션을 사용하는 것이 아니기 때문에 defaultValue 설정이 불가능하다.)
@RequestMapping(value = "login", method= {RequestMethod.POST})
// public String login(@RequestParam("id") String id, @RequestParam("password") String password) {
public String login(String userId, String password) {
// name 속성의 갑소가 매개변수의 명이 같으면 어노테이션 생략이 가능
public String login(String id, String password) {
log.info("login() - 호출 : {} {}", id, password);
return "home";
}
2-2. @RequestParam에 default 값 설정
- defaultValue 속성을 사용하면 파라미터 name 속성에 값이 없을 경우 기본값을 지정할 수 있다.
@PostMapping("/login")
public String login(@RequestParam("id") String id,
@RequestParam(value = "password", defaultValue = "0000") String password) {
log.info("login() - 호출 : {} {}", id, password);
return "home";
}
2-3. @RequestParam에 실제 존재하지 않는 파라미터를 받으려고 할 때
- 파라미터 name 속성에 없는 값이 넘어올 경우 에러가 발생한다.
- @RequestParam(required = false)로 지정하면 null 값을 넘겨준다.
- 단, defaultValue를 설정하면 defaultValue에 설정된 값으로 넘겨준다. (에러가 발생하지 않음)
@PostMapping("/login")
public String login(@RequestParam("id") String id,
@RequestParam(value = "password") String password,
// @RequestParam(value = "address", required = false)String address) {
@RequestParam(value = "address", defaultValue = "서울특별시") String address) {
log.info("login() - 호출 : {} {} {}", new Object[] {id, password, address});
return "home";
}
3. @PathVariable 어노테이션을 통해서 전송받기 (restAPI시 많이 사용)
- URL 패스상에 있는 특정 값을 가져오기 위해 사용하는 방법이다.
- REST API를 사용할 때, 요청 URL 상에서 필요한 값을 가져오는 경우 주로 사용한다.
- 매핑 URL에 {}로 묶는다면, {} 안의 값을 Path Variable로 사용하고 요청 시 실제 경로상의 값을 해당 Path Variable로 받겠다는 의미이다.
- 매핑 URL에 {} 안의 변수명과 매개변수의 변수명이 동일하다면 @PathVariable의 괄호는 생략이 가능하다.
(어노테이션 자체는 생략이 안된다. @RequestParam인지 @PathVariable인지 알 수 없음)
@GetMapping("/member/{id}")
// public String findMember(@PathVariable("id") String id) {
public String findMember(@PathVariable String id) {
log.info("Member ID : {}", id);
return "home";
}
4. @ModelAttribute 어노테이션을 통해서 전송받기 (파라미터가 많을 경우)
- 요청 파라미터가 많은 경우 객체 타입으로 파라미터를 넘겨받는 방법이다.
- 스프링 컨테이너가 기본 생성자를 통해서 객체를 생성하고
파라미터 NAME 속성의 값과 동일한 필드명을 가진 필드에 값을 주입해준다.
- 단, 기본 생성자와 Setter가 존재해야 한다.
- @ModelAttribute 어노테이션을 생략해도 객체로 매핑된다.
@PostMapping("/login")
// public String login(@ModelAttribute Member member) {
public String login(Member member) {
log.info("{}, {}", member.getId(), member.getPassword());
return "home";
}
*/
@Autowired
private MemberService service;
/*
* 로그인 처리
* 1. HttpSession과 Model(Controller가 처리하고 만들어진 데이터를 Dispatcher Servlet에게 전달해주는 것) 객체
* 1) Model
* - 컨트롤러에서 데이터를 뷰로 전달하고자 할 때 사용하는 객체이다.
* - 전달하고자 하는 데이터를 맵 형태(key, value)로 담을 수 있다.
* - Model 객체의 Scope는 기본적으로 Request이다. (하나의 요청을 받아서 응답될때까지만 유지)
*
@PostMapping("/login")
public String login(
HttpSession session, Model model,
@RequestParam("id")String id, @RequestParam String password) {
log.info("{}, {}", id, password);
Member member = service.login(id, password);
if (member != null) {
session.setAttribute("loginMember", member);
/*
* return "home";
* - forwarding 방식으로 ViewResolver에 의해 /WEB-INF/views/home.jsp로 forwarding 한다.
*
* return "redirect:/";
* - redirect 방식으로 여기서 리턴 한 경로로 브라우저에서 다시 요청을 하도록 반환한다.
*
*
return "redirect:/";
} else {
model.addAttribute("msg", "아이디나 비밀번호가 일치하지 않습니다.");
model.addAttribute("location","/");
return "common/msg"; // /WEB-INF/views/common/msg.jsp로 forwarding 한다.
}
}
// 로그아웃 처리
@PostMapping("/logout")
public String logout(HttpSession session) {
session.invalidate();
return "redirect:/";
}
2. @SessionAttributes과 ModelAndView 객체
1) @SessionAttributes("키값")
- Model 객체에 "키값"에 해당하는 Attribute를 Session Scope까지 범위를 확장시킨다.
2) ModelAndView
- 컨트롤러에서 뷰로 전달할 데이터와 뷰에 정보를 담는 객체이다.
- addAttribute()가 아닌 addObject() 메소드를 통해서 데이터를 담을 수 있다.
*/
@RequestMapping(value = "/login", method = {RequestMethod.POST})
public ModelAndView login(ModelAndView model,
@RequestParam("id") String id, @RequestParam("password") String password) {
log.info("{}, {}", id, password);
Member loginMember = service.login(id, password);
if(loginMember != null) {
model.addObject("loginMember", loginMember);
model.setViewName("redirect:/");
} else {
model.addObject("msg", "아이디나 비밀번호가 일치하지 않습니다.");
model.addObject("location", "/");
model.setViewName("common/msg");
}
return model;
}
// 로그아웃 처리 (SessionStatus 객체 사용)
@PostMapping("/logout")
public String logout(SessionStatus status) {
log.info("status.isComplete() : {}", status.isComplete());
// SessionStatus 객체의 setComplete() 메소드로 세션 스코프로 확장된 객체들을 지워준다.
status.setComplete();
log.info("status.isComplete() : {}", status.isComplete());
return "redirect:/";
}
// 회원가입
@GetMapping("/member/enroll")
public String enroll() {
log.info("회원 가입 페이지 요청");
return "member/enroll";
}
@PostMapping("/member/enroll")
public ModelAndView enroll(ModelAndView model, @ModelAttribute Member member) {
log.info(member.toString());
int result = service.save(member);
if(result > 0) {
model.addObject("msg", "회원가입이 정상적으로 완료되었습니다.");
model.addObject("location", "/");
} else {
model.addObject("msg", "회원가입을 실패하였습니다.");
model.addObject("location", "/member/enroll");
}
model.setViewName("common/msg");
return model;
}
/*
* @ResponseBody
* - 일반적으로 컨트롤러 메소드의 반환형이 String 타입일 경우 뷰의 이름을 찾아서 반환한다.
* - @ResponseBody 붙은 String 반환은 해당 요청을 보낸 클라이언트에 전달할 데이터를 의미한다.
*
* jackson 라이브러리
* - 자바 객체를 JSON 형태의 데이터로 변환하기 위한 라이브러리이다.(GSON, jsonSimple)
* - 스프링에서는 jackson 라이브러리를 추가하고 @ResponseBody을 사용하면 리턴되는 객체를 자동으로 JSON으로 변경해서 응답한다.
*
* @RestController
* - 해당 컨트롤러의 모든 메소드에서 데이터를 반환하는 경우 사용한다.
* - @Controller와 @ResponseBody를 합쳐놓은 역할을 수행한다.
*/
// json 테스트할 수 있는 컨트롤러 생성
@GetMapping("/member/jsonTest")
@ResponseBody
public Object jsonTest() {
// Map<String, String> map = new HashMap<>();
//
// map.put("hi", "hello");
//
// return map;
return new Member("mrhong", "1234", "홍길동");
}
// 아이디 중복체크
@PostMapping("/member/idCheck")
// @ResponseBody
// @ResponseBody를 사용하지 않고 ResponseEntity를 사용하는 방법 (jackson 라이브러리 추가하고 사용)
public ResponseEntity<Map<String, Boolean>> idCheck(@RequestParam("userId") String userId) {
Map<String, Boolean> map = new HashMap<>();
log.info("{}", userId);
map.put("duplicate", service.isDuplicateID(userId));
return new ResponseEntity<Map<String,Boolean>>(map, HttpStatus.OK);
}
}
> MemberServiceImpl.java
package com.kh.mvc.member.model.service;
import org.apache.ibatis.session.SqlSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.kh.mvc.member.model.dao.MemberMapper;
import com.kh.mvc.member.model.vo.Member;
//@Service("빈 ID")
@Service
//@Transactional
public class MemberServiceImpl implements MemberService {
@Autowired
private MemberMapper mapper;
@Autowired
private BCryptPasswordEncoder passwordEncoder;
// @Autowired
// private SqlSession session;
@Override
public Member login(String id, String password) {
Member member = null;
// member = dao.findMemberById(session, id);
member = mapper.findMemberById(id);
// System.out.println(mapper.findAll());
// System.out.println(passwordEncoder.encode(password));
// System.out.println(member.getPassword());
// System.out.println(passwordEncoder.matches(password, member.getPassword()));
// if (member != null && member.getPassword().equals(passwordEncoder.encode(password))) {
// return member;
// } else {
// return null;
// }
return member != null &&
passwordEncoder.matches(password, member.getPassword()) ? member : null;
}
@Override
@Transactional
public int save(Member member) {
int result = 0;
if(member.getNo() != 0) {
// update
} else {
// 패스워드 암호화
member.setPassword(passwordEncoder.encode(member.getPassword()));
// insert
result = mapper.insertMember(member);
}
// if(true) {
// throw new RuntimeException();
// }
return result;
}
@Override
public Boolean isDuplicateID(String id) {
return mapper.findMemberById(id) != null;
}
}
> mybatis-context.xml
<?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:c="http://www.springframework.org/schema/c"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource"
destroy-method="close"
p:driverClassName="${db.driver}"
p:url="${db.url}"
p:username="${db.username}"
p:password="${db.password}"
/>
<bean id="mybatisConfig" class="org.apache.ibatis.session.Configuration"
p:jdbcTypeForNull="NULL"
/>
<!--
p:configLocation="classpath:mybatis-config.xml"
p:configuration-ref="mybatisConfig"
-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"
p:configuration-ref="mybatisConfig"
p:mapperLocations="classpath:mappers/**/*.xml"
p:typeAliasesPackage="com.kh.mvc.*.model.vo"
p:dataSource-ref="dataSource"
/>
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate"
c:sqlSessionFactory-ref="sqlSessionFactory"
/>
<bean id="mapperScanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer"
p:basePackage="com.kh.mvc.*.model.dao"
/>
<!--
트랜잭션 처리 방법
1. 트랜젝션 매니저 등록하기
2. @Transactional을 사용해서 트랜젝션 처리가 될 수 있도록 <tx:annotation-driven />태그를 설정 파일(servlet-context.xml)에 등록한다.
-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
p:dataSource-ref="dataSource"
/>
</beans>
> servlet-context.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:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.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
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->
<!-- Enables the Spring MVC @Controller programming model -->
<annotation-driven />
<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
<!--
mapping 속성에 해당하는 요청에 대해 location 속성에 지정 된 디렉터리(폴더)로 매핑을 시켜준다.
-->
<resources mapping="/resources/**" location="/resources/" />
<!--
사용자로 부터 /js/jquery-3.6.0.js 라는 요청이 오면 이것을 /resources/js/jquery-3.6.0.js로 매핑시킨다.
-->
<resources mapping="/js/**" location="/resources/js/" />
<!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
<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>
<!-- @Component 클래스를 찾아서 Bean으로 등록 -->
<context:component-scan base-package="com.kh.mvc" />
<!--
<annotation-driven />, <context:component-scan base-package="com.kh.mvc" />을 사용하지 않을 경우
아래와 같이 HandlerMapping과 Controller를 명시적으로 bean으로 등록해야 한다.
각 요청을 수행할 Controller들의 클래스를 빈으로 등록하고
<beans:bean id="loginController" class="com.kh.spring.member.controller.LoginController"/>
<beans:bean id="logoutController" class="com.kh.spring.member.controller.LogoutController"/>
HandlerMapping에 각각의 Controller들을 매핑해야 한다.
<beans:bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<beans:property name="mappings">
<beans:props>
<beans:prop key="login.me">loginController</beans:prop>
<beans:prop key="logout.me">logoutController</beans:prop>
</beans:props>
</beans:property>
</beans:bean>
* 잘 안씀
-->
<!--
DB 관련 설정은 root-context.xml에서 import하는 mybatis-context.xml에 작성했지만
<tx:annotation-driven />는 실제 트랜젝션을 적용할 빈들이 등록되는 ApplicationContext 설정 파일에 작성해야 한다.
-->
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>
</beans:beans>
> security-context.xml
<?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:c="http://www.springframework.org/schema/c"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 암호화를 위한 BCryptPasswordEncoder 등록 -->
<bean id="bcryptPasswordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>
</beans>
'국비지원_Java > Java Programming_2' 카테고리의 다른 글
자바 프로그래밍_Day_111_게시글 목록 조회 (0) | 2022.02.22 |
---|---|
자바 프로그래밍_Day_110_회원 정보 수정, 회원 탈퇴 (0) | 2022.02.22 |
자바 프로그래밍_Day_108_로그인, 로그아웃, 회원가입 구현 (0) | 2022.02.18 |
자바 프로그래밍_Day_107_Spring MVC, mybatis 연동 (0) | 2022.02.15 |
자바 프로그래밍_Day_106_Spring AOP / Annotation (0) | 2022.02.07 |