소통 하고싶은 개발자

프로젝트 진행 ⑥ : 회원가입 기능 구현 + URL 매핑 개선 본문

토이 프로젝트/익명 채팅 사이트

프로젝트 진행 ⑥ : 회원가입 기능 구현 + URL 매핑 개선

OhPro 2022. 7. 15. 11:45
반응형

목차

  • 시작하며
  • 제작 페이지와 와이어 프레임 비교
  • 구현 방법
  • 테스트 코드 사용
  • URL 매핑 개선
  • 마치며

 


 

시작하며

뭔가 점점 포스팅을 하는 텀이 길어지는 것 같은 기분이 든다... 코딩은 매일 하고 있는데 뭔가 마음에 안드는 구석을 보고 쉽게 지나치지 못하는 성격때문이지 않을까 싶다..^^ 그래도 이전에는 몰라서 알아야겠다는 느낌으로 조사를 했었다면 점점 선택하는 시간이 짧아지는 느낌이 든다. 올바른 선택이어야 할텐데....!!

 


 

제작 페이지와 와이어 프레임 비교

 

설계 와이어 프레임

 

 

이용 약관 글 + 사이드바

 

위 사진은 약관 글과 사이드 바를 구현한 것이다. 약관 글은 대충 만들려다가 그래도 너무 대충은 좀 별로일 것 같아서 다른 회원가입 양식을 많이 참고했다.

 

약관 동의 라디오 박스

 

이용 약관은 펼쳐서 볼 수도 있고 접어서 볼 수 있게 만들었다.

 

계정 정보 입력 페이지

 

계정 정보 입력 페이지에서는 다른 페이지와는 다르게 자바스크립트(javascript)를 조금 사용해서 조금 더 유연하게 만들어봤다. 자세한건 아래에서 언급하겠다.

 

프로필 정보 입력 페이지

 

생각해보니 와이어 프레임을 만들 때 성별입력을 넣지 않았어서 라디오 버튼으로 추가해줬다. 라디오 버튼은 간단하게 설명하면 그룹 내에 선택할 수 있는 것이 단 하나인 체크박스이다.

 


 

구현 방법

먼저 회원가입 약관 페이지를 만들면서 약관 글은 정말 다 읽어보고 작성했다..^^ 약관 페이지를 구현하면서 가장 의미있는 부분을 꼽는다면 동의, 비동의 라디오 박스이다. 라디오 박스는 그룹 내에 단 하나의 선택 아이템을 가지는 컴포넌트인데 같은 그룹으로 묶어주기 위해 isAgree라는 이름의 그룹으로 묶었다.

 

약관 동의 폼

 

라디오 버튼은 name속성을 그룹id와 같은 방식으로 사용하다보니 위 코드에서 name 속성에 'isAgree'를 넣었고, 실제 선택된 라디오 버튼 값은 value속성으로 들어온다. 추가적으로 약관 글은 부트스트랩에서 아코디언이라는 컴포넌트를 사용하여 펼치기/접기 기능을 추가하여 스크롤을 아래로 내리지 않아도 접어서 다음 페이지로 넘어갈 수 있도록 설정하였다.

 

showAccountInputPage 메서드

 

위 사진은 약관 동의 페이지에서 다음 단계로 이동 버튼을 클릭했을 때 실행되는 컨트롤러의 코드이다.

 

하단에 세션 어트리뷰트 중 하나인 ACCOUNT_CERT_KEY(계정을 인증하는 키 값이라는 의미로 상수로 지정했음)를 초기화하는 부분을 볼 수 있다. 회원가입을 진행하면서 계정 정보를 입력하고 이메일로 인증을 실시하는 부분이 있는데, 이메일로 인증하는 도중에 사용자가 세션을 종료하지 않고 뒤로가기 후 회원가입을 재진행했을 경우에 인증과 관련된 정보들을 초기화 하기 위해 추가해줬다.

 

계정 정보 입력 과정 중

 

약관에 동의하고 다음 페이지로 넘어오면 계정 정보를 입력할 수 있는 폼이 나오는데 여기선 자바스크립트(javascript)를 사용해서 조금 편의성 기능을 추가했다.

 

대표적으로 아래 두 가지 기능을 추가했다.

 

  • 비밀번호 일치 여부 확인 기능
  • 아이디 중복 여부 확인 기능

 

비밀번호 일치 여부 확인 기능

 

위 코드는 비밀번호 일치 여부 확인 기능으로 코드는 다음 순서로 작성했다.

 

  1. 비밀번호와 비밀번호 확인 input태그를 불러온다.
  2. addEventListener() 함수를 사용해 타이핑이 일어날 때마다 checkPw() 함수가 실행되도록 이벤트를 등록해준다.
  3. 두 input태그의 값을 비교하여 일치 여부를 라벨로 알려주는데 이 때 두 input태그 모두 빈칸이면 라벨은 보이지 않도록 한다.

 

XmlHttpRequest

 

다음으로 아이디 중복 여부 확인 기능인데, 위 코드는 XmlHttpRequest를 사용하기 위해 method, uri, post할 data를 받아 비동기 송수신을 할 수 있도록 만든 함수이다.

 

리턴(return) 값으로 xhr자체를 받아서 요청했던 곳에서 넘어오는 데이터를 받을 때 이벤트 함수를 해당 함수를 사용하는 곳에서 작성할 수 있도록 작성했다. 왜냐면 위 사진처럼 4~5줄 남짓인데 데이터를 처리하는 부분데이터 수신 처리 부분을 분리하지 않는다면 함수를 재사용할 수 없게 되어버리기 때문이다. (재사용성 고려)

 

아이디 중복 여부 확인 기능

 

위 코드는 아이디 중복 여부 찾기 기능을 구현한 코드로 구현 방법은 다음과 같다.

 

  1. 위와 마찬가지로 이번에는 중복 확인 버튼 컴포넌트를 가져온다.
  2. 클릭 시 함수가 작동될 수 있도록 addEventListener() 함수를 사용한다.
  3. 클릭 시 값이 없을 경우 인터락으로 alert가 발생하고 종료된다.
  4. 값이 있었을 경우에는 작성한 XmlHttpRequest를 실행하여 입력된 아이디를 <key : value> 의 형태로 <'id' : '입력된 아이디'>를 JSON 값으로 송신한다.
  5. 요청 송신 뒤에 서버에서 아이디가 사용 가능한지 여부를 획득해 디스플레이 해준다. 

 

checkSameId 메서드

 

아이디 중복 여부 체크 요청을 수행할 컨트롤러가 실행되는 과정은 다음과 같다.

 

  1. Request에 함께 들어온 JSON 데이터를 JSONObject 형식으로 변환해준다. (여기서는 미리 만들어둔 파싱 유틸리티를 사용했음)
  2. 전달받은 JSONObject 객체에서 사용자가 입력했던 id 값을 받아온다.
  3. Service 객체를 사용하여 DB에서 해당 아이디를 사용하고 있는 유저가 있는지 체크 후 아이디 사용 가능 여부의 의미로 리턴 값을 boolean 형태로 받는다.
  4. 자바스크립트(javascript)에 JSON으로 보내줄 것이므로 JSONObject 형식의 객체를 만들어 아이디 사용 가능 여부를 넣어준다.
  5. 마지막으로 ViewInfo 객체를 생성 후 리턴을 해주는데 이때 뷰를 갖지 않는다는 의미로 setContainView(false)를 사용한다.

 

ViewInfo 자체가 본인이 직접 만든 객체이므로 클래스에서 boolean 형태의 멤버를 추가하고 setter 함수를 호출한 것이다. 나중에 디스패쳐 서블릿(이것도 본인이 직접 만든 객체)에서 ViewResolver가 동작할 때 getViewName() 메서드를 호출할건데 해당 메서드 내부에서 이 멤버의 값을 확인하고 적절히 리턴해준다.

 

방금 내용에서 직접 만든 객체들에 대해 궁금하신 분들은 링크를 타고 가면 내가 이전에 프로젝트를 진행하면서 ViewInfo디스패쳐 서블릿을 만들게된 경위를 볼 수 있게 해두었다!

 

getJsonObject 메서드

 

위에서 request 객체를 매개로하여 JSONObject 객체를 반환해주는 메서드의 내부로 메서드 진행은 다음과 같은 순서로 진행된다.

 

  1. BufferedReader 객체를 선언하여 request객체에서 getReader() 메서드로 BufferedReader를 받는다.
  2. JSONParser 객체의 parse() 메서드로 BufferedReader 객체를 JSONObject 객체로 변환시킨 뒤 리턴한다.

 

<dependency>
  <groupId>com.googlecode.json-simple</groupId>
  <artifactId>json-simple</artifactId>
  <version>1.1</version>
</dependency>

 

JSON를 사용하기 위해 googlecode.json-simple 라는 라이브러리를 사용했고 위 처럼 maven pom.xml에 넣어두었다. pom.xml을 작성하는 방법은 여기에 설명해놨다.

 

checkAccountCert

 

위 코드는 계정 정보 입력 페이지에서 모든 정보를 입력한 뒤 이메일 인증하기 버튼을 눌렀을 경우 실행되는 컨트롤러의 코드로 실행 과정은 다음과 같다.

 

  1. 세션 어트리뷰트 맵에서 ACCOUNT_CERT_KEY(상수)를 키 값으로 갖는 객체를 받는다.
  2. Service 객체를 호출하여 받아온 객체에서 인증번호 체크 및 데이터 getCertErrorMessage()메서드로 유효성 체크를 실시한다.
  3. 만약 불러온 객체가 null이거나 유효성 체크를 통과하지 못한 경우 errorMessage 변수에 오류 메세지를 반환해주어 유저가 데이터를 다시 입력할 수 있도록 유도한다.
  4. Service 객체의 createAccount()를 호출하여 메서드계정 등록을 실시하는데 로직적으로 성공/실패 여부를 successed 변수에 boolean 형태로 담는다.
  5. 페이지 구성 시나리오 상 이 과정이 정상적으로 종료되면 프로필 등록 페이지로 이동하는데 이 때 어떤 계정의 프로필을 작성할건지를 알려주기 위해 getCreatedAccountNo() 메서드로 등록했던 계정의 no(번호)를 가져와 accountNo 변수에 담는다.
  6. 계정 번호를 가져올 때 로직적으로 실패하면 -1을 반환하도록 해놓았으므로 번호가 유효한지 검증한다.
  7. 정상적으로 계정 번호까지 가져왔다면 이전에 사용했던 AccountCertDTO 객체를 세션에서 없애고 프로필 등록을 위한 계정 번호만 세션에 넣어준다.
  8. 마지막으로 계정 정보 등록은 정보의 등록이므로 다시 실행되면 안되기 때문에 리다이렉트를 시켜주기 위해 실제 url을 View의 이름으로 설정하여 setRedirectTo() 메서드로 리다이렉트 결과임을 뷰 리졸버에게 알린다.

 

setRedirectTo 메서드

 

위 과정 중 setRedirectTo() 메서드는 내부적으로 ViewInfo객체의 메서드인 setContainView(false), setViewName('url')을 사용하는 편의성 메서드이다.

 

createAccount 메서드

 

createAccount() 메서드의 내부인데, 원래 계획했던 프로젝트 시나리오는 익명으로 글을 게시하고 댓글을 달기 때문에 프로필 정보가 있어도되고 없어도 상관없는 구조를 만들어야 했다.

 

그래서 당연히 관리를 위해서라도 프로필 정보는 계정을 생성할 때 같이 생성되도록 해두고 추후에 프로필을 설정해도 되도록 구성하려고 생각했고, 위처럼 createDefaultUser()메서드를 해당 메서드 내에서 실행되도록 만들었다.

 

createDefaultUser 메서드

 

위에서 User객체는 Entity 객체로 생성자를 오버로딩하여 계정 번호만 넣었을 때 나머지 값들은 디폴트 값이 들어갈 수 있도록 설정했다.

 

글을 포스팅하는 와중에 User라는 Entity 객체의 클래스명을 Profile로 변경했다. (대량의 소스작업과 함께..^^) 변경한 이유는 아무리 생각해도 User라는 의미를 담기에는 그릇이 작은 느낌이 들어서였다. User라는 느낌보단 Profile에 대한 정보를 담고있는 객체라고 표현하는게 적절해보인다. User는 뭔가 계정을 사용하는 유저가 하는 모든 행동과 연관지어 생각하게 하기 때문이다.

 

만약 혼자 진행하는 프로젝트가 아니고 그룹 프로젝트였다면 회의나 미팅을 했을 것 같다. '객체 이름, 이대로 괜찮은가'에 대해서ㅎㅎ 그리고는 몰매를 맞으며 하던대로 하자는 팀원들의 질책을 받지는 않을까...^^

 

updateProfile

 

위 사진은 Profile을 작성하고 적용하기 버튼을 클릭했을 시 서버에서 실행되는 컨트롤러의 코드이다. 코드가 진행되는 순서는 아래와 같다.

 

  1. 사용자가 입력한 닉네임, 나이, 지역, 성별 정보를 불러온다.
  2. 계정 정보 입력 페이지에서 세션 어트리뷰트에 넣었던 계정 번호를 불러온다.
  3. 계정 번호가 없었을 경우에 index 페이지로 리다이렉트하는데, index 페이지는 로그인 세션이 존재하면 메인 페이지로 이동하도록 되어있다.
  4. 만약 프로필 정보를 등록함에 있어 문제가 생긴다면 로직적인 문제이므로 사용자에게는 안내 메시지가 디스플레이될 수 있도록 만들었다.
  5. 모든 과정이 정상적으로 종료되면 인덱스 페이지로 리다이렉트하면서 회원가입이 종료될 수 있도록 했다.

 


 

테스트 코드 사용

이번에 여러 기능을 테스트하면서 느낀 것이 있는데 항상 기능을 테스트하려고 모든 시퀀스를 이행해야 하는 것이 불편하다는 것이었다. 그렇게 별 생각없이 단지 불편하다고 생각만 하고 있다가 문득 pom.xml 에 등록되어있는 JUnit 라이브러리를 보는데 '이거 한번 써볼까?' 라는 생각을 했다.

 

원래는 원하는 결과가 나오면 테스트가 성공하고 그 이상 그 이하도 아니라고 생각했었는데 생각해보니 그게 아니었다. DAO, Service 객체의 메서드 테스트를 진행할 때 뭔가 전체 시퀀스를 실행하지 않고도 테스트를 이용함으로써 시간을 단축할 수 있었다.

 

테스트 코드

 

위 사진은 메서드를 테스트하기 위해 작성한 테스트 코드인데 저런식으로도 사용할 수 있고, 뭔가 다양한 사용 방법이 있을 것이라고 생각한다. 아직은 TDD 경험이 많지는 않지만 TDD의 좋은 점을 깨닫게 된 만큼 앞으로도 자주 사용할 것 같다.

 


 

URL 매핑 개선

사실 이번에 포스팅을 하면서 제일 골칫거리가 무엇인가라고 묻는다면 'URL 매핑 방식을 바꾼 것이 제일 골치였다'라고 말할 수 있을 것 같다. 단지 방식만 바꾼 것이 아니고 '어떤 방식으로 해야 효율적일지'에 대해서 꽤 생각하면서 수정에 들어갔는데, 정확히는 이전에 언급했던 Rest API를 적용해야 할지에 대한 내용이었다.

 

이전 포스팅에서는 분명 추가 기능 건으로 해보겠다 라고 언급했었다. 사실 갑작스럽게 이렇게 막무가내로 진행한 이유는 원래 HTML에서 <form> 태그를 사용해서 데이터를 송신할 때는 GET과 POST 메서드만 사용할 수 있다.

 

만약 Rest API를 적용해야 한다면, PUT, DELETE 메서드도 함께 사용해야 하는데 form 태그를 사용해서는 한계가 있었다. 그렇다면 자바스크립트(js)를 사용해서 이벤트로 XMLHttpRequest를 사용해서 비동기 통신을 하면 어떨까 하는 생각을 잠시 해보았다.

 

솔직히 아찔했다. 지금까지 만들었던 기능들을 모두 js로 변경해야 했기 때문이었다. 그래서 기능 추가건으로 한다고 했었는데 검색을 하면서 폼 태그의 하위에 <input> 태그를 만들고 hidden 속성을 사용해서 아래와 같이 하면 PUT, DELETE 메서드가 먹힌다는 것이었다.

 

<input hidden='_method' value='put' />

 

그런데 나의 경우 이렇게 해도 PUT 메서드가 적용되지 않았다. 원래 톰캣에서도 PUT, DELETE를 막도록 만든 설정이 있었고 해당 설정을 off시키는 방법을 알게되어 off 시켜 검증까지 했었기에 이건 html에선 되는데 jsp에선 안되는건지 이런식으로 갈피를 못잡고 있었다.

 

당연하겠지만 PUT, DELETE 메서드를 사용하지 못하면 Rest API를 구현할 수 없다. 한 가지 의문 점이었던 것은 스프링 프레임워크에서는 web.xml에 filter를 설정하고 위처럼 <input> 태그를 사용하면 PUT, DELETE 메서드를 사용할 수 있다는 것이었다.

 

url 네이밍 방식을 그대로 유지하기에는 너무 형식없이 만드는 느낌이고 특별히 누군가 기억하지 않으면 만약 인수인계를 진행하거나 다른 사람이 코드를 봐도 이해하지 못하는 내용이 되어버린다는 단점이 너무 치명적이었다.

 

토이프로젝트를 진행하면서 이런 단점을 극복하지 못하는건 항상 페이지를 하나하나 늘려갈 때마다 고민이었다. 그러던 중 묘안이 떠올랐는데 현재는 프론트 컨트롤러(Front Controller)패턴 구현을 내가 직접 했기 때문에 위 처럼 태그를 JSP에 적어주고 디스패쳐 서블릿에서 일차적으로 request.getParameter('_method') 메서드를 실행했을 때 값이 null이 아닌 경우 해당 값이 PUT, DELETE 중 하나라면 PUT, DELETE 메서드를 수신한 것처럼 하는 것이었다. 

 

PUT, DELETE 메서드 수신하기

 

위 사진은 위에서 언급했던 내가 생각한 방법을 코드로 옮긴 것이다. 이렇게 변경하고 put, delete 메서드를 수신하는 테스트를 했을 때 정상적으로 작동했다.... 

 

이렇게 CRUD 메서드를 사용할 수 있게됨에 따라 프로젝트 규모가 커지기 전에 얼른 URL 구조부터 변경하기로 했다. 

 

url 매핑 정보 excel file

 

위 사진은 URL 구조를 변경하기에 앞서 어떤식으로 Restful하게 네이밍할지 먼저 정리해놓은 엑셀 자료이다. 앞으로 페이지가 추가됨에 따라 Restful 하게 작성할 계획이다.

 


 

마치며

항상 이렇게 긴 포스팅을 작성하고나면 많은 생각을 가지고 있었구나 라는생각도 드는데 막상 전체 진행률은 크게 올라가지 않은게 아쉬운것 같다. 열심히 해야겠다!

 

 

감사합니다!!

반응형