IT 성장 일기

[Spring]쇼핑몰프로젝트04 - 로그인화면 모달로 구현하기(다른 페이지를 모달로 불러오기) 본문

인턴일기/쇼핑몰 프로젝트

[Spring]쇼핑몰프로젝트04 - 로그인화면 모달로 구현하기(다른 페이지를 모달로 불러오기)

통통배 노아 2022. 10. 9. 22:42

지난 시간에는 회원가입 페이지를 완성했다. 회원가입 양식을 작성할 때 서식과 같은 유효성을 검사하는 기능을 구현하였고, ajax를 사용하여 가입하고자 하는 아이디를 입력하는 동시에 중복성여부도 검사하는 기능을 구현했다.

2022.10.08 - [인턴일기/쇼핑몰 프로젝트] - [Spring]쇼핑몰프로젝트03 - ajax로 아이디중복검사, 회원가입 페이지 유효성검사

 

[Spring]쇼핑몰프로젝트03 - ajax로 아이디중복검사, 회원가입 페이지 유효성검사

2022.10.07 - [분류 전체보기] - [Spring]쇼핑몰프로젝트02 - 회원가입 기능 구현 [Spring]쇼핑몰프로젝트02 - 회원가입 기능 구현 2022.10.06 - [인턴일기/쇼핑몰 프로젝트] - [Spring]쇼핑몰프로젝트01 - 메인,..

step-by-step-noah.tistory.com

 

오늘은 메인 페이지(정확히 말하면 header부분에 있는 네비바)의 "로그인" 버튼을 눌렀을 때 로그인페이지를 모달로 불러와 로그인까지 하는 기능을 구현하고자 한다.

 

목표

1. 로그인페이지 모달로 불러오기 (외부 jsp파일을 모달로 부분적으로 띄우기)

2. 로그인 기능 구현

 

기능 구현1

1. 로그인페이지 모달로 불러오기 (외부 jsp파일을 모달로 부분적으로 띄우기)

우선 모달을 활용하는 방법자체는 어렵지 않았다.

그냥 부트스트랩에 있는 모달기능을 사용했는데, 구글링을 해보면 일반적으로 메인페이지 내에서 모달로 띄울 부분을 추가한다.

물론 이 방법이 쉽긴했다. 그냥 따라하니 5분 밖에 안걸림

 

문제는 로그인 페이지를 구현하는 만큼 로그인 모달부분을 header.jsp으로 부터 분리해 따로 loginForm을 만들어 구분해 주고싶었다.

 

모달기능 구현은 두가지 방식으로 진행했다.

1. 모달창을 불러오는 부모페이지 내부에 모달로 나오는 자식페이지가 있는경우(즉, 한 페이지 내에 모달을 불러오는 부분과 불러지는 부분이 모두 있는경우 ex) header.jsp 안에서 모두 구현) 

2. 모달창을 불러오는 부모페이지와 불러지는 자식페이지가 분리되어 있는 경우(페이지 2개->header.jsp에서 부르면 loginForm.jsp가 불려짐) --> 내가 원하는 방식

 

 

방식1. 한페이지 내에서 모달을 모두 구현하는 경우

이 경우는 그다지 어렵지 않았고 참고할 페이지도 많았다.

부트스트랩을 활용하여 구현했는데, 기본적인 폼은 아래와 같다.

이때 그림에 표시했듯이 빨간 부분은 빨간부분끼리, 초록색은 초록끼리 같아야 한다. 내용은 본인이 원하는 걸로 바꿔도 된다. 나는 로그인페이지를 띄울 것이라 loginModal로 해주었다. 

그리고 이제 Modal Body 부분에 모달로 띄우고자 하는 내용을 추가하면 된다.

 

추가 후 화면

모달 푸터 부분에도 약간의 변화를 주었다.

 

 

이렇게 간단한 방식으로 모달창을 띄울 수 있다.

 

 

그런데 나는 헤더 부분jsp 파일에 로그인 관련 페이지 코드를 사용하지 않고 싶었다.

뭔가 분리도 안되고 나중에 loginForm 수정할때 파일명이 header.jsp로 되어있으니 까먹을 것 같기도하고...

암튼 그래서 아까 ModalBody 부분에 추가해준 부분을 따로 loginForm.jsp로 떼어나기로 했다.

 

방식2. 모달창을 불러오는 부모페이지와 불러지는 자식페이지가 분리되어 있는 경우(페이지 2개->header.jsp에서 부르면 loginForm.jsp가 불려짐) 

참고한 글이다.

https://badstorage.tistory.com/

 

기억 조각 모음 저장소

 

badstorage.tistory.com

 

위의 글을 참고하였다.

1. 띄우고자하는 화면 분리하기

분리할때 주의할 점이 있다. 

아까 Modal Body 부분에 띡 추가해준 부분만 따로 빼는 것이 아니고

<div class"modal-content"> 부분까지만 남겨 두고 그 하위에 들어있는 영역들을 따로 빼주어야한다.

말로하면 어려우니 그림을 그렸다. 파란부분은 그대로 header.jsp에 남기고 빨간부분은 따로 빼서 loginForm.jsp로 넘겨주었다.

분리하고 나면 부모페이지인 header.jsp 는 아래와 같아진다. header.jsp 페이지가 훨씬 깔끔해 졌다.

변경 후 header.jsp

아래는 빨간 부분만 따로 뺀 loginForm.jsp이다.

2. 모달버튼 눌렀을 때 loginForm.jsp를 불러오기 위해 onclik 추가하기

여기서 openLoginModal()은 함수 이름이다.

 

3. 부모페이지(header.jsp)에 외부자식페이지 (loginForm.jsp) 를 불러오는 openLoginModal()함수 작성하기

처음 코드는 다음과 같다.

변경 전 function

아까 위에 올렸던 링크에서 참고하여 함수를 짰으나 계속해서 모달이 전혀 뜨지 않는 문제가 발생했다.

 

$('#loginModal .modal-content').load("/member/loginForm"); 이 부분에서 문제가 발생했다. 원래라면 header.jsp의

id가 loginModal인 부분의 class 명이 modal-content 인 영역에 "loginForm" 파일을 불러오겠다는 의미인데

 

여러 시도를 해본 결과,  #loginModal 이 아이디를 인식하지 못하고 있었다.

그럼 그냥 class명이 modal-content인 곳이나 찾아라~ 하고 #loginModal 부분을 삭제했더니 갑자기 모달 창이 정상적으로 떴다.

아 참고로 나게에게는 function의 두번째줄인 $('#loginModal').modal(); 이 부분도 있으나 없으나 정상적으로 잘 작동했기에 그냥 주석처리 해줬다.

변경 후 function

4. Controller 작성하기

아마 이렇게만하면 모달이 작동 하지 않을 것이다.

저 load() 부분이 바로 url부분이라 loginForm을 띄워주는 Controller를 작성해 주어야한다.

나는 Member 관련 데이터 처리를 하는 MemberController에 작성했다.

여기서 다른 사람들이  나의 코드를 보면 조금 헷갈릴만한게, 아까 위의 function 부분에서 load안에 써준 경로는 loginForm.jsp가 아니다. Controller의 좌표?이다.

나는 MemberController를 Member 관련 컨트롤러라고 확실히 구분하고 싶어서 컨트롤러에

@RequestMapping(value = "/member") 를 해주어 url에서 /member을 한번 더 타고 Controller로 들어가는것이다.

Controller에  RequestMapping해주지 않은 사람들은 그냥 function에서 load("/loginForm") 해주면된다.

 

전반적인 흐름은 load()에 찍힌 경로 타고 Controller로 이동-> Controller에서 요청받아 실제 loginForm.jsp 열어주기

이다.

 

이제 결과화면은 다음과 같다. 아주 정상적으로 모달이 작동한다.

 

내가 위에서 첨부한 링크와 내 글을 적절히 참고해서 모달창을 분리해보면 좋을 것 같다.

 

 

 

 

 


기능구현 2

2.Ajax로 로그인 기능 구현하기

이제 폼이 완성되었으니 아이디와 비밀번호를 입력했을 때 로그인이 되는 기능을 구현하고자 한다.

기능

1. 아이디,비밀번호를 입력하고 로그인 버튼을 눌러 일치하는 정보가 없으면 

"아이디 또는 패스워드가 틀렸습니다." 라는 문구가 바로 뜨게하기 (Ajax로 로그인 기능 구현하기)

2. 로그인 성공 시 메인페이지로 넘어가기

3.탈퇴한 회원이면 "이미 탈퇴한 회원입니다" 뜨기

 

1. 로그인 실패 시 모달창에 그대로 실패하였습니다 문구 뜨게하기

이걸 위해 Ajax를 사용할 것 이다. 바로바로 뜨면 기분이 좋으니..까?

1. jquery 사용할 것이니 cdn추가

내가 추가한 cdn이다

<script src="https://code.jquery.com/jquery-3.4.1.js"
	integrity="sha256-WpOohJOqMqqyKL9FccASB9O0KwACQJpFTUBLTYOVvVU="
	crossorigin="anonymous"></script>

 

2.loginForm.jsp에 ajax 작성

3.ajax의 url을 보면  member/login이라고 되어있다.

MemberController에 login메서드를 추가해주었다.

나는 admin과 member테이블을 따로 분류해두었기 때문에 로그인 메서드 호출과 동시에 session에서 member와admin을 모두 remove를 먼저 해주었다.

 

그런데 오류 발생

에러메세지가 떴다.

org.apache.ibatis.builder.BuilderException: Error parsing Mapper XML. The XML location is 'com/metanet/shopping/mapper/MemberMapper.xml'. Cause: org.apache.ibatis.builder.BuilderException: Error resolving class. Cause: org.apache.ibatis.type.TypeException: Could not resolve type alias 'com.metanet.shopping.model.MemberVO'.  Cause: java.lang.ClassNotFoundException: Cannot find class: com.metanet.shopping.model.MemberVO Could not resolve type alias

 

MemberMapper.xml에서 resultType인 MemberVo를 찾지 못해 발생한 오류로 보였다.

 

해결법

- servlet-context.xml파일에서 component-scan의 base-package를 모든 패키지가 포함되도록 수정해야한다.

1.servlet-context.xml파일 위치

src- > main ->webapp -> WEB-INF -> spring -> appServlet -> servlet-context.xml

-servlet-context.xml변경하기

- servlet-context.xml파일에서 component-scan의 base-package를 모든 패키지가 포함되도록 수정해야한다.

변경 전
변경 후

그래도 안된다....

알고보니 ㅋㅋㅋㅋ  mapper.xml에서 resultType이름이 잘못됐다... 헐

MemberVo 인데 마지막에 실수로  대문자 O를 사용했던것!!!! 수정하고 다시 실행보자..

ㅋㅋㅋㅋㅋㅋㅋ 드디어 정상적으로 작동한다... 미친걸까 나..?

심지어 memberpw=#{memberpw}도 오타남

그래도 덕분에 좋은것 알아간다.

 

 

결과화면

이제 모달창에서도 ajax를 통해 비동기 방식으로 로그인을 할 수있게 되었다.

존재하지 않는 아이디와 패스의 경우 바로바로 밑에 경고문구가 뜬다.

 

탈퇴한 회원에 대해서도 탈퇴한 회원이라는 문구가 뜬다.

정상적으로 로그인 시 알림창이 뜨며 메인페이지로 이동한다.

 

 

 

 

 


추가로 해당 에러에 대한것을 구글링하다가  알게된 사실

 

Object인 MemberVo에 NoArgsConstructor 가 없이 AllArgsConstructor만 제공되어 DefaultObjectFactory는 AllArgsConstructor에 파라메토로 xml 쿼리 결과값을 매핑해줌.

여기서 파라메터 맵핑방식이 이름기반이 아닌 쿼리 결과값의 순서이기 때문에 select절의 컬럼 순서와 Object 필드의 순서가 일치하지 않아  일치하지 않는 다른 값이 object에 맵핑되는 문제가 생김.

 

해결법

MemberVo에 

@NoArgsConstructor(access = AccessLevel.protected)

@AllArgsConstructor추가

이렇게 하면 DefaultObjectFactory가 디폴트 생성자로 object를 생성하고, 디폴트리절트셋핸들러가 파라미터 이름을 기반으로 결과를 맵핑해 주기때문에 문제가 해결된다.

 

해결 참고 글

https://yeti.tistory.com/278

 

MyBatis에서 resultType으로 Object 맵핑시 필드에 상이한 값이 맵핑되는 이슈

안녕하세요. yeTi입니다. 오늘은 Mybatis를 사용하면서 결괏값을 받은 Object의 값이 상이하게 맵핑되는 현상을 해결한 내용을 공유하고자 합니다. 현상 MyBatis 에서 sqlMapper 를 활용하여 결괏값의 Objec

yeti.tistory.com