📌스프링 컨테이너와 빈에 대해
- 스프링 컨테이너란?
자바 객체의 생명 주기를 관리하며, 생성된 자바 객체들에게 추가적인 기능을 제공하는 역할
자바 객체를 스프링에서는 빈(Bean)이라고 부름
스프링 컨테이너는 IoC와 DI의 원리를 이용 , 싱글톤을 유지
- 참고
1. 로그인
< header.jsp >
<form action="login.me" method="post">
<!-- Modal body -->
<div class="modal-body">
<label for="userId" class="mr-sm-2">ID : </label>
<input type="text" class="form-control mb-2 mr-sm-2" placeholder="Enter ID" id="userId" name="userId"> <br>
<label for="userPwd" class="mr-sm-2">Password : </label>
<input type="password" class="form-control mb-2 mr-sm-2" placeholder="Enter Password" id="userPwd" name="userPwd">
</div>
<!-- Modal footer -->
<div class="modal-footer">
<button type="submit" class="btn btn-primary">로그인</button>
<button type="button" class="btn btn-danger" data-dismiss="modal">취소</button>
</div>
</form>
<MemberController.java>
@RequestMapping(value="login.me") //RequestMapping타입을 어노테이션을 붙여줌으로써 HandlerMapping등록
public void loginMember() {
System.out.println("로그인요청");
}
** Spring에서 Parameter(요청 시 전달값)를 받는 방법
1) HttpServletRequest를 이용해서 전달받기(기존의 JSP/Servlet 때 방식)
해당 메서드의 매개변수로 HttpServletRequest타입을 작성해두면 스프링 컨테이너가 해당 메서드를 호출 시 자동으로 해당 객체를 생성해서 매개변수로 주입해줌
@RequestMapping("login.me") //value=생략가능, 하지만 value값에 들어가는 것
public String loginMember(HttpServletRequest request) {
String userId = request.getParameter("id");
String userPwd = request.getParameter("pwd");
System.out.println("userId : " + userId);
System.out.println("userPwd : " + userPwd);
return "main";
}
2) @RequestParam어노테이션을 이용하는 방법
request.getParameter("키")로 밸류를 뽑아오는 역할을 대신해주는 어노테이션
value속성의 밸류로 jsp에서 작성했던 name속성값을 담으면 알아서 해당 매개변수를 받아올 수 있다.
만약, 넘어온 값이 비어있는 형태라면 defaultValue속성으로 기본값을 지정할 수 있다.
@RequestMapping("login.me")
public String loginMember(@RequestParam(value = "id", defaultValue="aaa") String userId, @RequestParam(value="pwd")String userPwd) {
System.out.println("userId : " + userId);
System.out.println("userPwd : " + userPwd);
return "main";
}
3) @RequestParam 어노테이션을 생략하는 방법
단, 매개변수명을 jsp의 name속성값(요청 시 전달하는 값의 키값)과 동일하게 세팅해둬야 자동으로 값이 주입
단점으로는 위에서 사용했던 defaultValue속성은 사용할 수 없음
//같은 클래스 내에서 같은 매핑값을 가진 어노테이션이 존재해서는 안됨
@RequestMapping("login.me")
public String loginMember(String id, String pwd) {
System.out.println("userId : " + id);
System.out.println("userPwd : " + pwd);
Member m = new Member();
m.setUserId(id);
m.setUserPwd(pwd);
//Service쪽 메소드에 m을 전달하면서 조회
return "main";
}
4) 커멘드 객체 방식
해당 메서드의 매개변수로
요청 시 전달값을 담고자하는 VO클래스의 타입을 세팅 후
요청 시 전달값의 키값(jsp의 name속성값)을 VO의 필드명으로 작성
스프링 컨테이너가 해당 객체를 기본생성자로 생성 후 내부적으로 setter메소드를 찾아서
요청 시 전달값을 해당 필드에 담아줌 == setter주입
**반드시 name속성값과 담고자하는 필드명이 동일해야함 + 기본생성자가 꼭 있어야 함 + setter가 꼭 있어야 함
@RequestMapping("login.me")
public String loginMemer(Member m) {
Member loginUser = memberService.loginMember(m);
if(loginUser == null) { //로그인 실패 => 에러문구르르 requestScope에 담고 에러페이지로 포워딩
System.out.println("로그인실패");
}else {//로그인 성공 => loginUser를 sessionScope에 담고 메인페이지 url로 요천
System.out.println("로그인성공");
}
return "main";
}
- memberService 객체 생성하는 방법
@Autowired //맞는 타입(MemberSerice)을 연결해준다는 뜻
private MemberService memberService; //인터페이스타입으로 선언만 (다형성을 적용할 수 있다는 장점)
* 기존 객체 생성 방식
- 객체간의 결합도가 높아짐
(B클래스의 수정이 일어날 경우 B클래스를 의존하고 있던 A클래스도 하나하나 전부 다 바꿔줘야 함)
- 서비스가 동시에 매우 많은 횟수가 요청될 경우 그만큼 객체 생성된다.
@Autowired //맞는 타입(MemberSerice)을 연결해준다는 뜻
private MemberService memberService; //인터페이스타입으로 선언만 (다형성을 적용할 수 있다는 장점)
** 요청 처리 후 응답데이터를 담고 응답페이지로 포워딩 또는 url재요청 하는 방법
1. 스프링에서 제공 Model객체를 사용하는 방법
포워딩할 응답뷰로 전달하고자 하는 데이터를 맵형식(key-value)으로 담을 수 있는 영역
Model객체는 requestScope
단, setAttrubute가 아닌 addAttribute메소드를 호출해야함
@RequestMapping("login.me")
public String loginMember(Member m, Model model, HttpSession session) {
Member loginUser = memberService.loginMember(m);
if(loginUser == null) {//로그인 실패 => 에러문구 requestScope에 담아서 에러페이지로 포워딩
model.addAttribute("errorMsg", "로그인실패");
return "common/errorPage";
}else { //로그인 성공 => loginUser를 sessionScope에 담고 메인페이지 url로 요청
session.setAttribute("loginUser", loginUser);
//url재요청 방식 == sendRedirect
//redirect:요청할 url
return "redirect:/";
//localhost:8007/spring + "/"
}
2. 스프링에서 제공하는 ModelAndView객체를 사용하는 방법
Model은 데이터 key-value세트로 담을 수 있는 공간이라고 한다면
View는 응답 뷰에 대한 정보를 담을 수 있는 공간 => 단독사용불가!
Handler Adapter가 ModelAndView가 아니면 넘겨줌
Model과 View가 결합된 형태의 객체
단, Model객체는 단독사용이 가능하지만 View객체는 ModelAndView타입으로만 사용이 가능하다.
@RequestMapping(value="login.me")
public ModelAndView loginMember(Member m,
ModelAndView mv,
HttpSession session) {
Member loginUser = memberService.loginMember(m);
//Member m의 userId 필드 : 사용자가 입력한 아이디
//Member loginUser의 userId 필드 : 조회된아이디
//Member m의 userPwd : 사용자가 입력한 비밀번호(평문)
//Member loginUser의 userPwd필드 : DB에 기억된 암호화된 비밀번호
//BCryptPasswordEncoder 객체 matches()
//matchs(평문, 암호문)을 전달
//암호문에 포함되어있는 Salt값을 판단해서 Salt값을 더한 후 암호화를 진행하여 두 값이 같은지 비교 후 일치한다면 true반환
if(loginUser != null && bcryptPasswordEncoder.matches(m.getUserPwd(), loginUser.getUserPwd())) {
session.setAttribute("loginUser", loginUser);
mv.setViewName("redirect:/");
}else {//로그인 실패 => 에러문구 requestScope에 담아서 에러페이지로 포워딩
mv.addObject("errorMsg", "로그인실패");
mv.setViewName("common/errorPage");
}
return mv;
}
<MemberSeviceImpl>
@Autowired
private MemberDao memberDao;
//sqlSessionTemplate객체를 사용해서 bean등록을 했었음
@Autowired
private SqlSessionTemplate sqlSession; //기존의 mybatis의 sqlSession대체
bean으로 등록된 애들 중에 자료형 맞는 애들로 인젭션좀 해줘 & 와이어링
@Override
public Member loginMember(Member m) {
//SqlSessionTemplate객체를 bean으로 등록 후 @Autowired
//스프링이 사용 후 자동으로 객체를 알아서 반납시켜주기때문에
//내가 더이상 close() 호출할 일이없다.
return memberDao.loginMember(sqlSession, m);
}
<MemberDao>
public Member loginMember(SqlSessionTemplate sqlSession, Member m) {
return sqlSession.selectOne("memberMapper.loginMember", m);
}
<member-mapper.xml>
<mapper namespace="memberMapper">
<!-- 로그인용 쿼리문 -->
<select id="loginMember" parameterType="member" resultMap="memberResultSet">
SELECT
USER_ID
,USER_PWD
,USER_NAME
,EMAIL
,GENDER
,AGE
,PHONE
,ADDRESS
,ENROLL_DATE
,MODIFY_DATE
,STATUS
FROM
MEMBER
WHERE
USER_ID = #{userId}
AND
STATUS = 'Y'
</select>
- resultMap
<resultMap id="memberResultSet" type="member">
<result column="USER_ID" property="userId"/>
<result column="USER_PWD" property="userPwd"/>
<result column="USER_NAME" property="userName"/>
<result column="EMAIL" property="email"/>
<result column="GENDER" property="gender"/>
<result column="AGE" property="age"/>
<result column="PHONE" property="phone"/>
<result column="ADDRESS" property="address"/>
<result column="ENROLL_DATE" property="enrollDate"/>
<result column="MODIFY_DATE" property="modifyDate"/>
<result column="STATUS" property="status"/>
</resultMap>
2. 로그아웃
@RequestMapping("logout.me")
public String logoutMember(HttpSession session) {
session.invalidate();
return "redirect:/";
}
3. 회원가입
<c:when test="${empty loginUser}">
<!-- 로그인 전 -->
<a href="enrollForm.me">회원가입</a>
<a data-toggle="modal" data-target="#loginModal">로그인</a>
</c:when>
포워딩을 시켜줌 => 경로를 지정 못하기 때문에
@RequestMapping("enrollForm.me")
public String enrollForm() {
//WEB-INF/views/ member/ memberEnrollForm .jsp <=로 forwarding
return "member/memberEnrollForm";
}
회원가입 폼의 매핑값
<form action="insert.me" method="post">
<MemberController>
@RequestMapping("insert.me")
public String insertMember(Member m, Model model) {
사용자가 입력한 값을 Member객체로 받아왔을 때 여러가지 문제점이 발생했음
1. 한글깨짐문제발생 => web.xml에 스프링에서 제공하는 인코딩 필터 등록
2. 나이를 입력하지 않았을 경우 int자료형에 빈 문자열이 넘어오기 때문에 자료형이 맞지 않는 문제발생
(400 Bad Request error 발생)
=> Member클래스의 age필드의 int형 => String형으로 변경
반복되는 getter, setter, toString등의 메소드 작성코드를 줄여주는 다이어트 라이브러리 = Lombok
* Lombok 설치방법
1) 라이브러리 다운 후 적용(Maven pon.xml)
2) 다운로드 된 jar파일을 찾아서 설치(작업할 IDE를 체크)
C:\dev\apache-maven-3.8.6\repository\org\projectlombok\lombok\1.18.12
3) IDE재실행
@NoArgsContructor
@AllArgsConstrutor
@Getter
@Setter
@ToString
* Lombok 사용 시 주의사항
- uName, bTitle 같이 앞글자가 외자인 필드명은 만들지 말 것
즉, 필드명 작성시 최소 소문자로 두 글자 이상으로 시작할 것
=> EL표기법 사용시 내부적으로 getter메소드를 찾을 때
getuName(), getbTitle()이라는 이름의 메소드를 호출
lombok의 명명규칙 getName(), getBTitle()이라고 만들어줌
@Setter @Getter @ToString
public class Member {
3. 비밀번호가 사용자가 입력한 있는 그대로의 평문
Bcrypt방식을 사용할 것
=> 스프링 시큐리티 모듈에서 제공(pom.xml에 라이브러리 추가)
=> BCryptPasswordEncoder 클래스를 xml파일에 bean으로 등록
=> web.xml에 spring-security.xml파일을 로딩할 수 있도록 작성
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/spring/root-context.xml
/WEB-INF/spring/spring-security.xml <!--추가-->
</param-value>
</context-param>
root-context읽은 다음 우리가 만든 security파일 읽도록!
- bean등록 파일 만드는 방법(원래는 root-context에서 해도 됨)
spring폴더에 new > Spring Bean Configuration File > "파일이름명"
<spring-security.xml>
<bean class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" id="bcryptPasswordEncoder"/>
<MemberController>
- 암호화 작업(암호문을 만들어내는 과정)
@RequestMapping("insert.me")
public String insertMember(Member m, Model model) {
String encPwd = bcryptPasswordEncoder.encode(m.getUserPwd());
m.setUserPwd(encPwd);//Member객체에 userPwd필드에 평문이 아닌 암호문으로 세팅
int result = memberService.insertMember(m);
if(result>0) { //성공 => 메인페이지 url재요청
return "redirect:/";
} else {
model.addAttribute("errorMsg", "회원가입실패");
return "common/errorPage";
}
* 암호문으로 DB에 저장했으므로 로그인 했을 때 암호문과 평문을 매치해 조회해야함
BCryptPasswordEncoder 객체 matches()
- matchs(평문, 암호문)을 전달
=> 암호문에 포함되어있는 Salt값을 판단해서 Salt값을 더한 후 암호화를 진행하여 두 값이 같은지 비교 후 일치한다면 true반환
if(loginUser != null && bcryptPasswordEncoder.matches(m.getUserPwd(), loginUser.getUserPwd())) {
session.setAttribute("loginUser", loginUser);
mv.setViewName("redirect:/");
}else {//로그인 실패 => 에러문구 requestScope에 담아서 에러페이지로 포워딩
mv.addObject("errorMsg", "로그인실패");
mv.setViewName("common/errorPage");
}
return mv;
<MemberServiseImpl>
@Override
public int insertMember(Member m) {
//int result = memberDao.insertMember(sqlSession, m);
//sqlSessionTemplate 객체가 자동 commit해줌
return memberDao.insertMember(sqlSession, m);
}
<MemberDao>
public int insertMember(SqlSessionTemplate sqlSession, Member m) {
return sqlSession.insert("memberMapper.insertMember", m);
}
<member-mapper>
<!-- 회원가입용 쿼리문 -->
<insert id="insertMember" parameterType="member">
INSERT
INTO
MEMBER
(
USER_ID
,USER_PWD
,USER_NAME
,EMAIL
,GENDER
,AGE
,PHONE
,ADDRESS
)
VALUES (
#{userId}
,#{userPwd}
,#{userName}
,#{email}
,#{gender}
,#{age}
,#{phone}
,#{address}
)
</insert>
'클라우드 융합 Full-stack 웹 개발자 양성과정 > Spring' 카테고리의 다른 글
Spring - ModelAndView로 바꾸기, 게시판글쓰기, 삭제, 수정 (0) | 2022.12.27 |
---|---|
Spring - 마이페이지(수정), 회원탈퇴, 게시글리스트조회 (0) | 2022.12.23 |
Spring - 개발환경구성 (0) | 2022.12.20 |
MyBatis - 검색, filter기능 (0) | 2022.12.19 |
MyBatis - 환경구성, 로그인, 회원가입기능, 게시글리스트 조회, 게시글 상세조회 (0) | 2022.12.17 |