1일차 (1)

728x90

 최근까지 코딩하는 것에 대해 흥미도 잃고, 의지도 떨어지고 그러는 바람에 수업일지도 띄엄띄엄 작성하다가 마지막 공개글이 한 달 전인 것 같다. 그래서 해야지, 해야지 하고 미뤄두던 개인 프로젝트를 STS4로 해보려고 잡았다가 무엇때문인지 자꾸 에러가 나서 다 포기하고 싶어져서 그만둘 뻔 했었다.
 하지만 포기할 수는 없으니까 다시 마음잡고 모르겠으면 보고 따라하고 검색하고 그러는 것부터 차근차근 도전해보기로 했다.

나 자신, 화이팅!!!

 


 

0. 시작 전 설정(gradle, application 등)

  •  build.gradle
더보기

 해당 설정은 내가 한 것이 아니라 선생님이 수업하실 때 설정한 파일 내용을 복사해왔다.

 나중에 내가 익숙해진다면, 이것도 직접 설정해서 해보고싶다.

 

  • application.preoperties
더보기

 해당 설정은 나 혼자 검색해보면서 직접 해보려다가 에러가 계속 나는데 이유를 모르겠어서 멘붕이 왔었다.

 결국 설명들이 붙어있는 선생님이 수업하실 때 설정한 파일 내용을 복사해왔다.

 

  • log4jdbc~ / logback.xml
더보기

 해당 설정은 이해하지 못했다 ㅠ

 

 

1. dto

  • DB에 담을 회원의 정보를 담을 클래스
더보기
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

@Accessors(chain=true)
@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
public class MemberDto {
	private String mId;	// 아이디
	private String mName;	// 이름
	private String mEmail;	// 이메일
	private String mPw;	// 비밀번호
	private String gName;	// 게임 닉네임
	
	private String joinDate;	// 가입날짜
}
  • @Accessors(chain=true): chaining된 형태로 객체를 생성하거나 수정할 수 있다. admin 페이지를 만들거나 할 때 사용한다. (아직 잘 모르겠다. 나중에 admin 페이지를 만들어 볼 예정이라 넣어놓았다.)
  • @Builder: (아직 잘 모르겠다..)
  • @Data: getter & setter를 따로 만들 필요없이 자동으로 생성해준다.
  • @NoArgsConstructor: 기본 생성자를 만들어준다.
  • @AllArgsConstructor: 입력받은 값에 따라 생성자를 만들어준다.
  • 필드명은 파라미터명, 컬럼 명과 같아야 헷갈리지 않고 편하게 사용할 수 있다.

 

 

2. 회원가입

 2-0) 참고한 링크 ㄱ

 

회원가입 창 (현재 css가 전혀 없어서 거슬린다..테이블로라도 정렬하려고 했는데 테이블로 안 한다고 하길래 힘줘서 참는중 ㅠㅠ)

우선 뇌에 힘줘서 css는 안 넣었다.
 회원가입에서 꼭 넣어서 연습하고 싶었던 것은 비동기 통신으로 아이디 중복 검사를 하는 것이었다.
 그리고, 비밀번호 확인도 조금! 디테일하게 넣고 싶었다. (비밀번호 입력 전에 비밀번호 확인을 입력하려고 하거나 비밀번호 입력 후 비밀번호 확인을 눌렀다가 안 적는다던가 하는 그런 상황들)
 게임 관련 사이트로 할 예정이었기 때문에 인게임 닉네임도 선택 사항으로 넣고, 회원가입 창에서 취소하고 뒤로 가고싶은 경우를 위해 뒤로가기도 넣었다. (사실 뒤로가기는 인터넷에 있으니 따로 필요없을 것 같지만..!)

 

 2-1) body

  • form 태그
  • 다른 홈페이지들의 회원가입 페이지를 많이 뜯어봐야 할 것 같다. 대충 구조는 알겠는데, 이것보다 나은 구조를 짤 수 있을 것 같은데 잘 모르겠다 ㅠㅠ
  • 그리고 class가 box_tip_title인 div는 p태그로 적은 부분을 제목(선 사이에 있는)으로 해서 안에 있는 요소들을 선으로 감싸는 구조로 하고 싶었는데 찾을 수 없었다..나중에 다시 찾아봐야겠다.
더보기
<form id="join_frm" method="post" action="/member/join" onsubmit="return joinCheck()">
	<div class="big-box j-b-box">
		<div class="box_tip_title">
			<p class="tip_title">필수 입력 사항</p>
			<div class="frm_item">아이디
				<input type="text" id="mId" name="mId" class="input" placeholder="아이디 입력" value="" maxlength="20" autocapitalize="off">
				<div id="chId" style="display: none;"></div>
			</div>
			<div class="frm_item">이름
				<input type="text" id="mName" name="mName" class="input" placeholder="이름 입력" value="" maxlength="10" autocapitalize="off">
			</div>
			<div class="frm_item">이메일
				<input type="text" id="mEmail" name="mEmail" class="input" placeholder="이메일 입력" value="" maxlength="40" autocapitalize="off">
			</div>
			<div class="frm_item" id="joinPwDiv">
				비밀번호
				<input type="password" id="mPw" name="mPw" class="input" placeholder="비밀번호 입력" value="" maxlength="20">
				<br>비밀번호 확인
				<input type="password" id="chMPw" name="chMPw" class="input" placeholder="비밀번호 재입력" value="" maxlength="20">
				<div id="chPw" style="display: none;"></div>
			</div>
		</div>
		<div class="box_tip_title">
			<p class="tip_title">선택 입력 사항</p>
			<div class="frm_item">인게임 닉네임
				<input type="text" id="joinMGName" name="joinMGName" class="input" placeholder="인게임 닉네임 입력" value="" maxlength="40" autocapitalize="off">
			</div>
		</div>
	</div>
	<br>
	<input type="button" value="뒤로가기" onclick="history.back()">
	<button type="submit" class="btn btn-3 btn-3e" id="joinSubBtn" style="text-align: center;">join the membership!</button>
</form>

 

 2-2) js

더보기
let useId = false;	// id 체크용
let usePw = false;	// pw 체크용
  • jquery를 여럿으로 나누어 작성했기 때문에 나중에 회원가입 버튼을 눌렀을 때 간편하게 확인하기 위해 필드에 boolean으로 선언해두었다.

 

$('#mId').on('keyup blur',function(){		// 중복 아이디 체크
	let id = $('#mId').val();
	document.getElementById("chId").style.display = 'block';
	if (id==''){
		$('#chId').html('아이디를 입력해주세요.').css('color','red');
		$('#mId').focus();
		return;
	}
	let chIdSend = {mId:id};
	console.log("chIdSend: ",chIdSend);
	$.ajax({
		method:'get',
		url: '/member/idCheck',
		data: chIdSend,
	}).done(function(res){
		console.log("res: ",res);
		if(res=='ok'){
			$('#chId').html('아이디 사용 가능!').css('color','blue');
			useId = true;
		}else{
			$('#chId').html('아이디 사용 불가!').css('color','red');
			useId = false;
		}
		
	}).fail((err,status)=>{
	console.log("err:", err);
	console.log("status:", status);
	useId=false;
	})
});
  • 중복 아이디를 ajax 비동기로 통신하기 위한 코드
  • jquery 이벤트로 keyup(키를 누르고 뗄 때 실행), blur(입력 양식에서 마우스 커서가 떼어지면 실행) 두 개를 동시에 사용했다.
  • 비동기 통신을 하기 전에 blur를 통해 회원가입 할 아이디 값이 빈 칸이라면 아이디 칸에 커서를 옮기고 return하도록 했다.
  • return되지 않았다면 비동기 통신을 위해 ajax 구조를 입력했다.
  • 첫번째 괄호, 대괄호에는 method(보낼 방법: get/post), url(보낼 위치), data(보낼 데이터)를 입력한다.
  • 뒤에 오는 두번째 코드는 갖다 오는 것에 성공하면 실행할 코드로, 받아온 데이터(매개변수)에 따라 아이디가 사용가능한지 아닌지 input mId 밑에 있는 div안 글씨를 바꾼다.
  • 실패 시 세번째 코드가 받는다.

 

$('#chMPw').on('click',function(){	// 비밀번호 체크
	let pw = $('#mPw').val();
	let chPw = $('#chMPw').val();
	let con = document.getElementById("chPw");
	con.style.display = 'block';
	if (pw==''){
		$('#chPw').html("비밀번호를 먼저 입력해주세요.").css("color",'red');
		$('#mPw').focus();
		return;
	}
})
$('#chMPw').blur(function(){
	let pw = $('#mPw').val();
	let chPw = $('#chMPw').val();
	let con = document.getElementById("chPw");
	con.style.display = 'block';
	if (pw!='' && chPw==''){
		$('#chPw').html("비밀번호 확인 칸은 비워둘 수 없습니다.").css("color",'red');
		$('#chMPw').focus();
		return;
	}
})
$('#joinPwDiv').on('keyup', function(){
	let pw = $('#mPw').val();
	let chPw = $('#chMPw').val();
	let con = document.getElementById("chPw");
	con.style.display = 'block';
	if (pw!=''){
		if(pw.length<=20 && pw.length>=8){
			$('#chPw').html("");
		}else if(pw.length>20 || pw.length<8){
			$('#chPw').html("비밀번호의 길이는 8~20로 정해주세요.").css("color",'red');
			$('#mPw').focus();
			return;
		}
		if(chPw!='' && pw!=chPw){
			$('#chPw').html("비밀번호가 다릅니다.").css("color",'red');
			$('#chMPw').focus();
		}else if(pw==chPw){
			usePw = true;
			con.style.display = 'none';
		}
			return;
	}
	usePw = false;
});
  • 첫번째 코드는 비밀번호 확인 칸을 클릭했을 때 비밀번호 칸이 비워져있다면 비밀번호 칸에 커서를 옮긴다.
  • 두번째 코드는 비밀번호 확인 칸을 클릭했다가 빈 칸인 상태(채웠다가 지운 경우도)에서 다른 곳을 클릭 시 비밀번호 확인 칸을 비울 수 없다며 비밀번호 확인 칸에 커서를 옮긴다.
  • 세번째 코드는 비밀번호/비밀번호 확인 칸에서 키보드가 눌렸다가 떼어질 때 비밀번호 칸의 길이, 비밀번호 칸과 비밀번호 확인 칸의 동일값인지 확인한다.

 

function joinCheck(){
	let name = $('#mName').val();
	let email = $('#mEmail').val();
	let pw = $('#mPw').val();
	let chPw = $('#chMPw').val();
	let gName = $('#mGName').val();
	
	if (!useId){
		alert("아이디를 확인해주세요.");
		$('#mId').focus();
		return false;
	}else if(name==''){
		alert("이름을 입력해주세요.");
		$('#mName').focus();
		return false;
	}else if(email==''){
		alert("이메일을 입력해주세요.");
		$('#mEmail').focus();
		return false;
	}else if(!usePw){
		alert("비밀번호를 확인해주세요.");
		$('#mPw').focus();
		return false;
	}
	alert("회원가입 성공!")
	return true;
}
  • 모든 칸의 값을 저장해두고, 조건에 따라 커서를 움직이고 리턴한다.
  • 모든 조건에 해당하지 않는다면 회원가입 성공 알림창을 띄우고 form 안의 정보들을 전송한다. 

 

 2-3) controller

더보기
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.myPro.persP.service.MemberService;
import lombok.extern.slf4j.Slf4j;

@RestController		// asynchronous(비동기) 를 받아주는 컨트롤러
@Slf4j
public class MemberAsyController {
	@Autowired
	private MemberService mSer;
	
	@GetMapping("/member/idCheck")
	public String idCheck(@RequestParam(name = "mId") String mId) {
		System.out.println(mId);
		log.info("==> GetMapping - idCheck 요청: ", mId," <==");
		return mSer.idCheck(mId);
	}
}
  • ajax 비동기 통신을 위해 만든 MemberAsyController
  • @RestController는 asynchronous(비동기)를 받아주는 컨트롤러다.
  • 가져올 매개변수에 @RequestParam에 mId(가져올 값의 키?)를 적어주지 않으면 에러가 나며 비동기 통신에 실패한다.
  • MemberService에 매개변수를 넣어 idCheck 메소드를 실행해 리턴 값을 그대로 리턴해준다.

 

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import com.myPro.persP.dto.MemberDto;
import com.myPro.persP.service.MemberService;

import lombok.extern.slf4j.Slf4j;

@Controller
@Slf4j
public class MemberController {
	@Autowired
	private MemberService mSer;
	
	@GetMapping("/member/joinfrm")
	public String join() {
		log.info("==> GetMapping - joinfrm 요청 <==");
		return "join";
	}
	
	@PostMapping("/member/join")
	public String join(MemberDto mDto, RedirectAttributes ra) {
		boolean result = mSer.join(mDto);
		if(result) {
			ra.addFlashAttribute("msg","회원가입 성공!");
			return "redirect:/member/loginfrm";
		}
		ra.addFlashAttribute("msg","회원가입 실패");
		return "redirect:/";
	}
	
	@GetMapping("/member/loginfrm")
	public String login() {
		log.info("==> GetMapping - loginfrm 요청 <==");
		return "login";
	}
	@PostMapping("/member/login")
	public String logrin() {
		return "login";
	}
}
  • MemberController
  • login은 아무 기능 없이 임시로 만들어두었다.
  • GetMapping으로 joinfrm을 요청하는 것은 메인 화면에서 들어오는 링크로, join.jsp를 리턴해준다.
  • PostMapping이 진정한 회원가입을 위한 기능인데, 여기서 메시지를 저장했는데 사용하는 것을 잊었다..
  • form 태그에서 보내온 데이터를 알아서 MemberDto로 저장하고, 이 mDto를 MemberService를 통해 boolean으로 회원가입(DB전송) 성공여부를 받고, 성공여부에 따라 보낼 위치를 따로 지정해 리턴한다.

 

 2-4) service

더보기
@Autowired
private MemberDao mDao;

 

public boolean join(MemberDto mDto) {
	BCryptPasswordEncoder pwEn = new BCryptPasswordEncoder();
	mDto.setMPw(pwEn.encode(mDto.getMPw()));
	
	return mDao.join(mDto);
}
  • 회원가입을 위한 메소드
  • BcryptPasswordEncoder를 통해 비밀번호를 암호화해주고, MemberDao의 join을 실행하고 리턴 값을 그대로 리턴해준다.

 

public String idCheck(String mId) {
	log.info("===> mSer checkId 요청: ",mId," <===");
	boolean result = mDao.idCheck(mId);
	System.out.println("==== mSer -> result: "+result);
	if(!result) {
		return "ok";
	}
	return "no";
}
  • 중복 아이디 체크를 위한 메소드
  • MemberDao에 id 검색을 실행하고 성공 값을 boolean으로 받아, 실패, 성공 시에 따라 "ok",  "no"를 리턴해준다.

 

 2-5) dao

더보기
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

import com.myPro.persP.dto.MemberDto;

@Mapper
public interface MemberDao {
	boolean join(MemberDto mDto);
	
	boolean idCheck(String mId);
}
  • MemberDao

 

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" 
"http://mybatis.org/dtd/mybatis-3-mapper.dtd" >

<mapper namespace="com.myPro.persP.dao.MemberDao">
	<insert id="join" parameterType="MemberDto">
		insert into member values(#{mId},#{mName},#{mEmail},#{mPw},#{gName},default)
	</insert>
	
	<select id="idCheck" parameterType="String" resultType="boolean">
		select count(*) from member where mId=#{mId}
	</select>
	
</mapper>
  • MemberDao의 xml 파일 (mappers)
  • MemberDao.java의 이름이 id값이 되고, parameterType은 받아올 타입이다. 받아올 타입은 알아서 인식할 수 있기 때문에 생략해도 된다.
  • resultType은 리턴해줄 타입이다. 안 적어도 java 파일의 타입을 통해 알아서 인식할 수도 있다. (생략하는 것을 추천하지 않는다고 하셨던 것 같다.)

 

 

3. 결과? 스크린샷 화면들

더보기
  • 아이디에 아무것도 입력하지 않고, 다른 곳을 클릭했을 경우

 

  • 아이디를 사용할 수 있을 경우

 

  • 아이디를 사용할 수 없는 경우

 

  • 이름, 이메일 입력 없이 회원가입 버튼을 눌렀을 경우

 

  • 비밀번호 입력 없이 비밀번호 확인 칸을 클릭했을 경우

 

  • 비밀번호 길이가 부족하거나 넘었을 경우

* 이때 길이를 충족하지 않은 상태로 비밀번호 확인 칸 입력 시 비밀번호 칸으로 이동한다.

 

  • 비밀번호가 다를 경우

 

  • 비밀번호가 같은 경우

 

  • 회원가입 성공!

* DB 확인

1, 3번은 이전에 실험용으로 넣은 데이터다.

2번이 막 일지를 정리하면서 넣은 데이터!

 


 

틀리거나 헷갈렸던 점 메모

  • ajax
  • js에서 display (none, block) 제어

 


 

전체 피드백

  • 타임리프 안 쓸건데 처음 시작 때 타임리프 넣어서 에러나고 제이쿼리 링크 안 긁어와서 제이쿼리 안 먹고 정말 다사다난했다 ㅠㅠ
  • sql 구문에서도 resultType을 parameterType으로 적어놓고 뭐가 문젠지 모르고, 매개변수도 #${mId} 이런 식으로 적어놓고 에러나야 알고 그랬다.
  • 역시 해봐야 익숙해지는 것 같다. 빨리 이거 끝내고 여러개 더 만들어봐야겠다. 비슷하더라도 여러번 해봐야 익숙해져서 나중에는 if문, for문 익숙해지는 것 마냥 편하게 사용할 테니까..!
  • 그래도 뭔가 해결되니까 그나마 하고싶어지는 마음은 생기는 것 같다! 슬럼프(?) 극복하기..!

 


 

 

728x90
1