반응형

오늘은 홈페이지에서 필수적인 파일 업로드이다

 

파일업로드는 커뮤니티 쇼핑몰.. 등등 모든 사이트에 다 들어가는 거같다.

안들어가는 사이트 있으면 알려주실뿐~~?? 😎

 

그래서 스프링이나 다른 언어를 공부할때 많은 라이브러리를 사용을 하는데

메이픈 레포지토리에서 다운 받을 수 있는 

Apache Commons FileUpload 

Apache Commons IO

 

이 2가지를 이용하여 파일 업로드를 구현을 해보자

 

시작🤩

 


 

1. 준비물 세팅

 

https://mvnrepository.com/

위 사이트에서 

 

Apache Commons FileUpload 

Apache Commons IO

 

2 가지를 코드를 카피한다.

 

참고로 

 

Apache Commons FileUpload 는 서블릿 3.0이상 부터 사용이 가능하다.

 

 

		<!-- 서블릿 3.0이상 사용 가능한 파일 업로드 api-->
		<dependency>
		    <groupId>commons-fileupload</groupId>
		    <artifactId>commons-fileupload</artifactId>
		    <version>1.3.1</version>
		</dependency>
		
		<!-- commons-io -->
		<dependency>
		    <groupId>commons-io</groupId>
		    <artifactId>commons-io</artifactId>
		    <version>2.4</version>
		</dependency>

 

레거시 프로젝트 pom.xml에서 넣어주고 꼭 메이븐 업데이트(alt + F5)

를 눌러서 꼬옥! 업데이트를 해주자

 

1-2 객체 등록 및 빈(Bean) 등록

 

Web-INF  > config(Spring) > Servlet-config(이름다를 수 있음!)

위 경로에 객체 및 빈 등록을 해주자 

 

<beans:bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
      
      <beans:property name="maxUploadSize" value="10485760" />
      <beans:property name="defaultEncoding" value="utf-8" />
   
   </beans:bean>

이렇게 빈등록을 해주자 

 

반드시 id를 multipartResolver로 선언

 

 

 최대 업로드 가능한 바이트 크기(바이트 단위), -1은 제한이 없음을 의미 -
      <beans:property name="maxUploadSize" value="10485760" />
      
       업로드 요청을 변환할 때 사용할 문자 인코딩 방식 
      <beans:property name="defaultEncoding" value="utf-8" />

 

이 두가지는 꼭 기억해주자 

 


2. 본격적인 코딩😋

 

2-1 JSP 

 

	<!-- 파일 업로드에서는 enctype(인코딩타입)을 multipart/form-data로 반드시 설정 -->
	<form action="upload_ok" method="post" enctype="multipart/form-data">
		파일 선택 : <input type="file" name="file">
		<input type="submit" value="전송">
	</form>
	
	<br><hr><br>
	
	<!-- 파일 두개이상 붙히는거 -->
	<form action="upload_ok2" method="post" enctype="multipart/form-data">
		파일 선택 : <input type="file" multiple="multiple" name="files"> 
		<input type="submit" value="전송">
	</form>

연습이기 떄문에 디자인은 생략한다

 

이때

🤓

중요한게 form태그안에 

enctype="multipart/form-data를 입력 해주어야한다.

 

참조 코딩의 시작 TCP School

 

2-1 Class VO 등 제작


import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@ToString
public class UploadVO {
	private String nmae;
	private MultipartFile file;
}

 

@Getter
@Setter
@ToString
public class MultiUploadVO {
	private List<UploadVO> list;
}

 

파일을 올릴때 한가지면 VO만 작성해도 되지만

2개 이상일 시는 LIst 타입으로 받아준다.

 

2-2 ConTroller 제작

 

//업로드로 가는 메소드
	@GetMapping("/upload")
	public void form() {}
	
	@PostMapping("/upload_ok")
	public String upload(@RequestParam("file") MultipartFile file) {
		String fileRealName = file.getOriginalFilename(); //파일명을 얻어낼 수 있는 메서드!
		long size = file.getSize(); //파일 사이즈
		
		System.out.println("파일명 : "  + fileRealName);
		System.out.println("용량크기(byte) : " + size);
		//서버에 저장할 파일이름 fileextension으로 .jsp이런식의  확장자 명을 구함
		String fileExtension = fileRealName.substring(fileRealName.lastIndexOf("."),fileRealName.length());
		String uploadFolder = "C:\\test\\upload";
		
		
		/*
		  파일 업로드시 파일명이 동일한 파일이 이미 존재할 수도 있고 사용자가 
		  업로드 하는 파일명이 언어 이외의 언어로 되어있을 수 있습니다. 
		  타인어를 지원하지 않는 환경에서는 정산 동작이 되지 않습니다.(리눅스가 대표적인 예시)
		  고유한 랜던 문자를 통해 db와 서버에 저장할 파일명을 새롭게 만들어 준다.
		 */
		
		UUID uuid = UUID.randomUUID();
		System.out.println(uuid.toString());
		String[] uuids = uuid.toString().split("-");
		
		String uniqueName = uuids[0];
		System.out.println("생성된 고유문자열" + uniqueName);
		System.out.println("확장자명" + fileExtension);
		
		
		
		// File saveFile = new File(uploadFolder+"\\"+fileRealName); uuid 적용 전
		
		File saveFile = new File(uploadFolder+"\\"+uniqueName + fileExtension);  // 적용 후
		try {
			file.transferTo(saveFile); // 실제 파일 저장메서드(filewriter 작업을 손쉽게 한방에 처리해준다.)
		} catch (IllegalStateException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return "fileupload/upload_ok";
	}

우선 Jsp에서 파일의 name = file로 지정해주었기 떄문에 

@RequestParam("file")로 가져온다. 

 

다음에 getOriginalFilename()로 파일을 업로드하면서 업로드한 파일명을 얻어낸다.

그 후 size를 알고 싶으면 file size를 붙혀 알아낸다. 

여기서 int를 붙히지않는다 용량이 커질 수도 있기 떄문에 

int값을 못 받을 수 있다.

 

물론 int로 받고 빈등록에서 용량을 조절해도 된다 

 

이때 나는 지정한 fileRealName(가져온 파일의 이름)에 확장자를 얻고 싶다.

fileRealName에서 subString을 사용하여  파일명을 조각낸다.

그 후 indexof로 (.)을 지정한 뒤 fileRealName.length()로 하면

예를 들면 이미지 파일일 경우

xxx.jpg 의 jpg를 따오게 된다. 

그래서 fileExtension에는 확장자가 들어가 있다. 

 

String uploadFolde= "C:\\test\\upload";을 지정해 주고 

위에 경로와 동일한 폴더를 생성해준다. 

이곳으로 업로드한 파일을 보낼것이다

 

 

		UUID uuid = UUID.randomUUID();
		System.out.println(uuid.toString());
		String[] uuids = uuid.toString().split("-");
		
		String uniqueName = uuids[0];
		System.out.println("생성된 고유문자열" + uniqueName);
		System.out.println("확장자명" + fileExtension);

그리고 컨트롤러에 왠 UUID 일 수가 있는데

컴퓨터는 동일한 파일명을 저장할 수 없다. 

이렇게 UUID를 생성하여 랜덤한 파일명의 이름으로 저장해준다.

 

File saveFile = new File(uploadFolder+"\\"+uniqueName + fileExtension);  
		try {
			file.transferTo(saveFile); // 실제 파일 저장메서드(filewriter 작업을 손쉽게 한방에 처리해준다.)
		} catch (IllegalStateException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return "fileupload/upload_ok";
	}

그리고 saveFile의 이름으로 아까 생성한 경로에 파일이 들어갈 수 있도록 생성해 준뒤

file.transferTo(saveFile);로 파일을 전송해준다. 

 

이때 파일전달에 예외를 던지고 있어 예외처리를 해준다. 

 


파일이 2개 이상 업로드 될 시🤡 

위의 경우는 1가지일 때이다.

 

 

VO는 동일하다.

 

 

 

Jsp 파일이 달라지게 되는데.

 

	<form action="upload_ok2" method="post" enctype="multipart/form-data">
		파일 선택 : <input type="file" multiple="multiple" name="files"> 
		<input type="submit" value="전송">
	</form>

 

 

multiple="multiple"을 써주게 되면

 

이렇게 바뀌게 된다🤓 

 

 

컨트롤러에서 추가된 부분을 찾아보자!

 

@PostMapping("/upload_ok2")
	public String upload2(MultipartHttpServletRequest files) {
		

		//서버에서 저장 할 경로
		String uploadFolder = "C:\\test\\upload";
		List<MultipartFile> list = files.getFiles("files");
		for(int i = 0; i<list.size(); i++) {
			String fileRealName = list.get(i).getOriginalFilename();
			long size = list.get(i).getSize();
			
			System.out.println("파일명 :" + fileRealName);
			System.out.println("사이즈" + size);
			
			File saveFile = new File(uploadFolder + "\\" + fileRealName);
			try {
				list.get(i).transferTo(saveFile);
			} catch (IllegalStateException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	
		return "fileupload/upload_ok";
	}

기본적인 형식을 똑같으나.

MultipartHttpServletRequest : 다중파일을 받아 올 수 있다. 

 먼저 와 같이 서버에 저장할 경로를 지정해 준뒤 

List로 파일을 받아오자

List<MultipartFile> list = files.getFiles("files") 파일 여러개라 리스트로받고
반복문을 돌린다. 하나씩 뺴줘야하니까.

 

그 후 반복문 중간에 UUID를 넣어 중복을 막아주면 된다(선택사항)

 

 


3.동작확인

 

파일 등록 후 전송

 

잉 오류 떳다.... ㅇ ㅏ 뭐지 하다가

곰곰히 영문을 보니 아까 빈으로 등록했던 파일의 용량보다 커서 예외처리가 되었다.

그러면 적은 파일을 넣어보자.

 

 

드디어 성공 

자 이제 아까 확인용 println을 보자.

 

아주 잘 가져오는것을 확인 할 수 있다.

uuid로 임의로 변경된 이름으로 저장이 되었다.

 

성공 !!!!

 

 

이렇게 오늘은 파일 업로드를 알아 보았는데

나중에 현업으로 가면 많이 쓰일 것 같다

생각보다 어렵지 않아서 좋당

 

코딩을 배우시는분들 화이팅!!

 

 

 

주인장 Git : https://github.com/MoonSeokHyun

 

MoonSeokHyun - Overview

http://mls0000.dothome.co.kr/. MoonSeokHyun has 7 repositories available. Follow their code on GitHub.

github.com

그럼 이만 !

 

반응형
반응형

현재 29살 신입으로 들어가기에는 늦은 나이(?) 라고 하기에는 아직 젊다고 생각한다.

특히 한국에서는 @살때 대학교 졸업 > @살때 취업 이런 루트의 개념의 사상이 현대인들에게 뿌리깊에

잠들어있다.  핀란드 및 다른 외국들을 다녀보면서 다른 나라에서는 위에 설명한 개념이 별로 통하지 않는다.

30살때 대학교 신입으로 들어가기 

@@살때 다른 것에 도전하기 

 

여자친구에게 물어보니 한국은 이것에 대해 정말 심각하게 생각한다고 한다. 

 

그래서 나도 별다른 걱정없이 늦은나이는 아니지만 20대 마지막 후반에 

6년간의 직무를 버리고 새로운 길을 찾아 떠난다. 

 


왜 직무를 떠낫나?

 

핀란드를 다니고 와서 나는 공황장애를 정말 심하게 겪엇다. 

지인의 추천으로 괜찮은 스타트업 인사팀에서 일했다. 연봉도 나쁘지 않았다. 

 

하지만 일을 할때도 평소의 컨디션이 나오지않고 계속 감정기복이 심했다. 

같이 협업을 하더라도 굉장히 경직되어있는 경우가 많았다. 

 

이때 스타트업이 개발관련 직군이여서 정말 많은 개발자를 보았다. 

그 떄 느낀것이 개발자는 굉장히 프리하고 또 멋있다. 라는 것이었다. 

 

그리고 친형이 개발자로 일하며 보았던 것을 생각하면서 개발자는 정말 양날의 검이라는 것을 생각하게 되었다. 

 

왜냐? 

 

흔히 SI직군이라는 개발자의 무덤이라고 생각한다. 

 

잦은 야근 및 클라이언트의 잦은 변경 항상 잦은 잦은.. 

이 잦은이라는 단어가 신물이 느껴질 정도로 개발자들은 갉아 내기 시작한다. 

그래서 개발자가 회사에 오래 남아있지 않는다. 

 

하지만 요세 스타트업들은 

나라에서 굉장한 지원금을 지원해주기도 하고 급여 또한 만만치 않다. 

그만큼 일의 강도를 강하지만 급여나 카페테리아 등으로 보상을 받는 것 같았다. 

 

위의 2가지의 사례를 모두 직 간접적으로 경험을 하였기 때문에 

개발자라는 직군이 내가 가게될 길이라는걸 불과 몇달전 까지 생각하지 못했다.

 

이런 것들을 신경쓰는 인사부서 및 경영팀 특성상 

항상 사람들의 신경을 살펴야하고 경영진과 가장 가까운 곳에서 서포트를 해야한다. 

 

하지만 이떄 나는 공황장애 및 정상적인 사회생활을 할 수없었다. 

 

스타트업에서 보던 개발자 들을 공경하고 그들을 공감하기 시작하면서 나의 개발자에 대한 꿈이 늘어갔다. 

 


독학 VS 학원 

 

나는 회사를 퇴사하고 해외에서 3개월 정도 시간을 보낸 뒤 

해외에서 JS 및 HTML을 경험해보며 생각보다 괜찮은데? 라고 느꼇다. 

이때 까지만 해도 간단한 취미 생활이었다. 

 

귀국 후 

 

개발자인 형에게 물어봤다 독학인가 학원인가? 

형의 추천은 국비 학원이었다.

 

한창 뉴스에서는 개발자 붐이라고 한참 떠들고 있어서 

나는 걱정이 되었다. 학원에 발품을 팔아야했던것이다.

 

2021년 9월 자가격리를 끝나고 본격적으로 학원을 알아보기 시작했다. 

 

내가 원하는 당시 원하는 조건은 이랬다. 

1. 비대면 수업이 아닌 대면수업 일것

2. WEB개발 위주의 학습일것

 

이 두가지 였다. 

 

하지만 JAVA 과정을 2번의 경우를 충족했지만

1번이 충족되는 경우는 거의 없었다. 

 

내가 알아본 학원은 

 

 

흔히 국비계의 3대장 비X 쌍X KX정보교육원

이 세가지를 우선적으로 보았다. 

 

1. 비X교육센터

 

대기가 정말 길었다. 9월 기준으로 11월에 개강되는 반이 있고 이것도 대기라고 했다. 

그리고 강남에 학원이있어 너무 멀어서 패스했다.

 

2.쌍X교육센터

 

면접을 보아야했다. 개강이나 모든 조건에 부합이되어 전화를 걸어 교육센터에서

특정일에 면접을 본다고 하였다. 

나는 ??? 학원 다니는데 면접?? 이런 생소한 경험에 놀랐다. 

면접을 보고 합격을 하였다.

하지만 개발자인 형이 커리큘럼을보고 아두이노가 들어가서 패스하게 되었다.

 

3. KX교육센터 

 

이곳도 강남까지 가서 학원에 대해서 매니저에게 듣고 면접을 본다고 하였다. 

내가 원하던 학원은 종로에 있어 종로에서 면접을 보았는데 

면접은 인성면접 위주였던걸로 기억한다. 

하지만 합격 후에 개강이 11월 약 2달뒤라 패스하게 되었다. 

개인적으로 KX학원을 희망 했지만 앞으로는 당겨 줄 수 없다고 했다. 

 

4. 중X정보통신학원

 

내가 현재 다니고 있는 학원이다. 

처음 커뮤니티에서 광고를 보고 전화를 걸었다. 

이대에서 간단한 상담을 1시간 정도 하고 퀘스트를 내주었다.

자바 상속까지 공부해서 올것!

하지만 이떄 너무 바빳다. 건강검진 및 다리부상 등으로.

학원에서 확인차 전화가 왔다. 

나는 답변하지 못했다. 

이렇게 탈락되는 줄 알았는데 합격이었다.

 

10월 19일날 개강하여 3월 31일 까지 아주 최적의 학원 스케쥴까지 

자바 + 파이선교육으로 커리큘럼또한 괜찮았다. 

 

이렇게 나의 개발자의 도전이 시작되었다.

 

반응형
반응형

요즘사이트에서 회원가입을 하다보면 꼭!  각종 인증들 이있다.

사용자 입장에서는 굳이?? 이걸 해야하나? 싶기도 하겠지만 

사업자 입장에서보면 각종 Black Consumer 외 다른 부분에 대해서 

보안을 강조한다. 그렇기 때문에 휴대폰인증 이메일인증은 꼭 필요하다고 말하고 싶다. 

 

why 이메일인증?? 😞

 

JUST 

 

비싸니까..

 

이유는 단순하다 그냥 프로젝트에 대해서 돈이들기 떄문이다.스프링에서 기본으로 제공하는 API로 충분한 이메일 인증이 가능하기 떄문이다!

 

자 이제 시작해보자!


 

1. API 다운 

 

https://mvnrepository.com/

 

https://mvnrepository.com/에서 javax.mail과 spring-context-support을 다운받아 준다. 

 

<!-- https://mvnrepository.com/artifact/javax.mail/mail -->
<dependency>
    <groupId>javax.mail</groupId>
    <artifactId>mail</artifactId>
    <version>1.4.7</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.springframework/spring-context-support -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context-support</artifactId>
    <version>5.3.9</version>
</dependency>

여러분의 시간은 소중하니까. 이걸 pom.xml 라이브러리에 추가해준다.

 

👻  나름의 꿀팁  👻 

<version>${org.springframework-version}</version> 이거 써주면 알아서 버전 맞춰준다! 

 

 


2. JSP 수정(HTML , JS 영역)

 

<div class="form-group email-form">
	 <label for="email">이메일</label>
	 <div class="input-group">
	<input type="text" class="form-control" name="userEmail1" id="userEmail1" placeholder="이메일" >
	<select class="form-control" name="userEmail2" id="userEmail2" >
	<option>@naver.com</option>
	<option>@daum.net</option>
	<option>@gmail.com</option>
	<option>@hanmail.com</option>
	 <option>@yahoo.co.kr</option>
	</select>
	/div>   
<div class="input-group-addon">
	<button type="button" class="btn btn-primary" id="mail-Check-Btn">본인인증</button>
</div>
	<div class="mail-check-box">
<input class="form-control mail-check-input" placeholder="인증번호 6자리를 입력해주세요!" disabled="disabled" maxlength="6">
</div>
	<span id="mail-check-warn"></span>
</div>

기존의 Jsp에서 본인인증 버튼을 추가하고 인증번호를 받을 자리를 남겨 두었다. 😁

 

2-1 JS 작성

 

$('#mail-Check-Btn').click(function() {
		const eamil = $('#userEmail1').val() + $('#userEmail2').val(); // 이메일 주소값 얻어오기!
		console.log('완성된 이메일 : ' + eamil); // 이메일 오는지 확인
		const checkInput = $('.mail-check-input') // 인증번호 입력하는곳 
		
		$.ajax({
			type : 'get',
			url : '<c:url value ="/user/mailCheck?email="/>'+eamil, // GET방식이라 Url 뒤에 email을 뭍힐수있다.
			success : function (data) {
				console.log("data : " +  data);
				checkInput.attr('disabled',false);
				code =data;
				alert('인증번호가 전송되었습니다.')
			}			
		}); // end ajax
	}); // end send eamil

우선 회원가입 도중에 진행되는 부분이라 비동기통신인 ajax를 진행한다.

GET방식으로 진행하며 기존에 있던 name 파라미터 값을 그대로 컨트롤러로 옮기려고한다. 

 


3. SPRING 부분 

 

 

//이메일 인증
	@GetMapping("/mailCheck")
	@ResponseBody
	public String mailCheck(String email) {
		System.out.println("이메일 인증 요청이 들어옴!");
		System.out.println("이메일 인증 이메일 : " + email);
	}

 

Get방식으로 가져오며 이메일을 확인 할 수있게 간단하게 프린트로 찍어준다. 

이때 변수의 이름이 동일하면 @붙는 아노테이션을 지정 안할수 있다.

 

굳 아주 잘 들어오는 것을 알 수있다.

 

컨트롤러에서 다 작성해주어도 되지만 컨트롤러가 너무 비대해지기 때문에 따로 MailSendService라는

CLASS를 하나 만들어서 진행하려고 한다. 

 

클래스는 만들었다면 빈등록을 해주어야한다. 

 

Spring 폴더에 있는 경로로 빈 xml 파일을 만들어 주어 설정값을 해주었다.

 

XML 빈등록

 

   <context:property-placeholder location="classpath:/db-config/email.properties" />

   <!-- 이메일 인증 관련 빈 등록 -->
   <bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
      <property name="host" value="smtp.gmail.com" />
      <property name="port" value="587" />
      <property name="username" value="${email.account}" />
      <property name="password" value="${email.password}" />
      <property name="javaMailProperties">
         <props>
                <prop key="mail.transport.protocol">smtp</prop>
                <prop key="mail.smtp.auth">true</prop>
                <!-- gmail의 경우 보안문제 업데이트로 인해 SSLSocketFactory를 추가해야 smtp 사용 가능. -->
                <prop key="mail.smtp.socketFactory.class">javax.net.ssl.SSLSocketFactory</prop>
                <prop key="mail.smtp.starttls.enable">true</prop>
                <prop key="mail.debug">true</prop>
                <prop key="mail.smtp.ssl.trust">smtp.gmail.com</prop>
				<prop key="mail.smtp.ssl.protocols">TLSv1.2</prop>
            </props>
      </property>
      
   </bean>

각 property의 값

host : 이메일 보낼 url을 입력

port : 포트 번호 입력

userName : 이메일을 보낼 아이디 입력

Password : 이메일 비번

 

아래 키값은 보안 업데이트로 따로 추가 해 주어야한다. 

 

아이디 비밀번호가 노출되는게 싫은 경우 file을 따로 만들어서 빈 주입후 따로 사용이 가능하다. 

 

다시 돌아와서 

 

MailSendService.class 제작🥰

 

@Component
public class MailSendService {
	@Autowired
	private JavaMailSenderImpl mailSender;
	private int authNumber; 
	// 난수 발생(여러분들 맘대러)
	
		public void makeRandomNumber() {
			// 난수의 범위 111111 ~ 999999 (6자리 난수)
			Random r = new Random();
			int checkNum = r.nextInt(888888) + 111111;
			System.out.println("인증번호 : " + checkNum);
			authNumber = checkNum;
		}
		
		
				//이메일 보낼 양식! 
		public String joinEmail(String email) {
			makeRandomNumber();
			String setFrom = ".com"; // email-config에 설정한 자신의 이메일 주소를 입력 
			String toMail = email;
			String title = "회원 가입 인증 이메일 입니다."; // 이메일 제목 
			String content = 
					"홈페이지를 방문해주셔서 감사합니다." + 	//html 형식으로 작성 ! 
	                "<br><br>" + 
				    "인증 번호는 " + authNumber + "입니다." + 
				    "<br>" + 
				    "해당 인증번호를 인증번호 확인란에 기입하여 주세요."; //이메일 내용 삽입
			mailSend(setFrom, toMail, title, content);
			return Integer.toString(authNumber);
		}
		
		//이메일 전송 메소드
		public void mailSend(String setFrom, String toMail, String title, String content) { 
			MimeMessage message = mailSender.createMimeMessage();
			// true 매개값을 전달하면 multipart 형식의 메세지 전달이 가능.문자 인코딩 설정도 가능하다.
			try {
				MimeMessageHelper helper = new MimeMessageHelper(message,true,"utf-8");
				helper.setFrom(setFrom);
				helper.setTo(toMail);
				helper.setSubject(title);
				// true 전달 > html 형식으로 전송 , 작성하지 않으면 단순 텍스트로 전달.
				helper.setText(content,true);
				mailSender.send(message);
			} catch (MessagingException e) {
				e.printStackTrace();
			}
		}
		
	
}

🥰

makeRandomNumber()

이메일을 보낼때 인증번호로 111111~999999까지의 난수를 발생시킨다.

 

public String joinEmail(String email)

컨트롤러에서 아이디가 넘어오면서 붙을 스트링값

메일 샌드로 보내줄 준비를 마친다.

 

public void mailSend(String setFrom, String toMail, String title, String content) {

 

 

MimeMessage message = mailSender.createMimeMessage(); 객체를 생성해준다.

이것이 스프링에서 제공하는 메일 API이다.

 

여기서 중요한 점은 빈 자동등록을 위해 @component를 해준다. 

 

 

컨트롤러 마무리😊

 

	
	@Autowired
	private IUserService service;
	@Autowired
	private MailSendService mailService;
	


	
	//회원가입 페이지 이동
	@GetMapping("/userJoin")
	public void userJoin() {}
	
	//이메일 인증
	@GetMapping("/mailCheck")
	@ResponseBody
	public String mailCheck(String email) {
		System.out.println("이메일 인증 요청이 들어옴!");
		System.out.println("이메일 인증 이메일 : " + email);
		return mailService.joinEmail(email);
		
			
	}

우선 아까 만들어둔 mailsendService 부분을 등록해준다. 

AutoWired로 자동 빈등록을 해준다. 

 

이제 Jsp파일 > 컨트롤러로 이메일값을 전달해주면 리턴값으로 Email을 전달하면서 

아까 만들어둔 메일을 발송해준다.

 


🤪 JS 마무리!🤪 

아까 만들어 둔 Jsp파일에 Js부분을 마무리해준다. 

 

$('#mail-Check-Btn').click(function() {
		const eamil = $('#userEmail1').val() + $('#userEmail2').val(); // 이메일 주소값 얻어오기!
		console.log('완성된 이메일 : ' + eamil); // 이메일 오는지 확인
		const checkInput = $('.mail-check-input') // 인증번호 입력하는곳 
		
		$.ajax({
			type : 'get',
			url : '<c:url value ="/user/mailCheck?email="/>'+eamil, // GET방식이라 Url 뒤에 email을 뭍힐수있다.
			success : function (data) {
				console.log("data : " +  data);
				checkInput.attr('disabled',false);
				code =data;
				alert('인증번호가 전송되었습니다.')
			}			
		}); // end ajax
	}); // end send eamil
	
	// 인증번호 비교 
	// blur -> focus가 벗어나는 경우 발생
	$('.mail-check-input').blur(function () {
		const inputCode = $(this).val();
		const $resultMsg = $('#mail-check-warn');
		
		if(inputCode === code){
			$resultMsg.html('인증번호가 일치합니다.');
			$resultMsg.css('color','green');
			$('#mail-Check-Btn').attr('disabled',true);
			$('#userEamil1').attr('readonly',true);
			$('#userEamil2').attr('readonly',true);
			$('#userEmail2').attr('onFocus', 'this.initialSelect = this.selectedIndex');
	         $('#userEmail2').attr('onChange', 'this.selectedIndex = this.initialSelect');
		}else{
			$resultMsg.html('인증번호가 불일치 합니다. 다시 확인해주세요!.');
			$resultMsg.css('color','red');
		}
	});

Ajax부분을 우선 완성해준다! 

 

error는 없다 왜냐하면 완벽하다! 

제발..

 

우선 funtion(data)값으로 아까 보내둔 인증번호가 왔을 것이다. 

이제 이것을 비교해 주면서 회원가입을 처리해주면 될것이다. 

 

 

인증번호 어서오고

이제 받은 data값으로 인증번호를 비교 하면 된다 .

위의 코드를 참조 바란다. 

 


🤩동작 확인🤩

 

우선 이메일을 보낼 주소를 입력해주고

 

alert창이 나를 반긴다.
!?
오오 .. 인증번호는 376236이라고 한다. 이제 입력해보자!
우선 거짓말을 해보았다. 역시 거짓말은 안통한다.
콤퓨타가 준 코드를 넣었다. 이제 반응한다. 쨔식... 

 

 

이거 좀 어려워서 한두번 정도 더 반복을 해보아야겠다. 

 

항상 지적은 환영! 

 

위의 코드를 쓴 깃 주소이다 . 한번쯤 방문해보자!

 

https://github.com/MoonSeokHyun/spring/tree/master/MyWeb/src

반응형
반응형

오늘은 회원가입이나 쇼핑몰 사이트를 만들 때 주로 필수 적인 

다음 카카오에서 제공하는 API를 이용하여 주소를 등록해보려고 한다. 

 


1. 우편번호 사이트로 이동

 

https://postcode.map.daum.net/guide

 

Daum 우편번호 서비스

우편번호 검색과 도로명 주소 입력 기능을 너무 간단하게 적용할 수 있는 방법. Daum 우편번호 서비스를 이용해보세요. 어느 사이트에서나 무료로 제약없이 사용 가능하답니다.

postcode.map.daum.net

🤬물론 도로명개발자 센터에서 제공하는 API가 있지만 실제로 사용해보면 🤬

다음 주소가 훨씬 많이 쓰이고 쓰기가 편하다.

 

카카오 주소 제공

 


2. 사용법

 

JS API의 최신화는 업데이트시마다 자동으로 되고 있습니다.
JS API호출시 도메인 뒤에 특정 파라미터의 경우엔 가이드페이지(아래 기본사용법)에서 정의하고 있는 특정 파라미터 외엔 허용하지 않습니다.
이외 더미 파라미터와 같은 데이터를 붙혀서 로딩하실 경우, 스크립트 호출이 거부 될수 있으니 삭제 부탁드립니다.
ex) ?_=XXXXXX, ?t=5469821879 등등

 

- 허용하는 파라미터
1. 통합로딩방식 : 없음
2. (구)로딩방식 : autoload=false
ㄴ ex) https://ssl.daumcdn.net/dmaps/map_js_init/postcode.v2.js?autoload=false
ㄴ ex) http://dmaps.daum.net/map_js_init/postcode.v2.js?autoload=false

 

한번 읽어 주자 

 

2-1 통합 로딩방식

 

<script src="//t1.daumcdn.net/mapjsapi/bundle/postcode/prod/postcode.v2.js"></script>

위와 같은 로딩 url을 서비스할 view 파일 (PHP, JSP)파일에 body 하단 부분에 넣어주자

 

 

2-2 이제 어떤 방식으로 할지 고르자

 

팝업을 이용하여 도로명 주소와 지번 주소 모두 보여주기

기본적인 팝업을 이용하여 사용자가 선택한 주소의 도로명 주소와 지번 주소를 모두 보여주는 방식입니다. 아래 '우편번호 찾기' 버튼을 클릭해서 바로 확인해보세요. (몇몇 Webview기반 브라우저의 window.open 미대응으로 인하여, Webview기반 서비스에 적용시 embed()를 이용한 레이어모드를 추천합니다.)

 

   

사용자가 선택한 값 이용하기

우편번호와 주소필드에 사용자가 선택한 주소 값을 채워 넣는 방식입니다.
단, 사용자가 선택한 값을 이용할 경우 영문주소와 정확히 매칭되지 않을 수 있습니다. 영문주소는 기본주소(address)에 해당하는 영문 주소가 내려갑니다. 예제 코드보기

 

 

iframe을 이용하여 레이어 띄우기

모바일웹에서는 팝업을 띄우는게 부담스러울 수도 있으니, 아래 코드와 같이 특정 element에 크기를 지정하여 iframe으로 끼워넣는 방식을 이용할 수도 있습니다. 아래 '우편번호 찾기' 버튼을 클릭해서 바로 확인해보세요.
(현재 iOS 8.x 이상, Safari, webview 브라우저에서 position:fixed, inner-scroll 이용시 가상키보드 터치 불량 및 스크롤이 멈추는 현상이 간혈적으로 발생하고 있습니다. 이점 참고해 주시기 바라며, 모바일 환경에서는 가급적 아래의 "페이지에 끼워넣기" 예제를 참고하시는 것을 추천드립니다.)
예제 코드보기

 

 

iframe을 이용하여 페이지에 끼워 넣기

화면내에 끼워넣는 방식일 경우, 내부 스크롤이 거슬릴수도 있습니다. 이때 onresize 속성을 추가해서 iframe 높이를 조절하면 스크롤이 생기지 않습니다. 아래 '우편번호 찾기' 버튼을 클릭해서 바로 확인해보세요. 예제 코드보기

 

 

주소를 선택하면 지도도 함께 보여주기

다음 지도 API를 함께 활용하여 선택한 주소에 대한 좌표를 가져와서 지도에 표시하는 예제입니다. 아래 '주소 검색' 버튼을 클릭해서 바로 확인해보세요.
다음 지도 API를 이용하시려면 https://developers.kakao.com에서 API Key를 발급받으셔야 합니다. 예제 코드보기

 

 

2-3 골랏다면 적용 해보자!

 

나는 제일 깔끔한

사용자가 선택한 값 이용하기

을 사용하기로 했다 

 

<input type="text" id="sample6_postcode" placeholder="우편번호">
<input type="button" onclick="sample6_execDaumPostcode()" value="우편번호 찾기"><br>
<input type="text" id="sample6_address" placeholder="주소"><br>
<input type="text" id="sample6_detailAddress" placeholder="상세주소">
<input type="text" id="sample6_extraAddress" placeholder="참고항목">

<script src="//t1.daumcdn.net/mapjsapi/bundle/postcode/prod/postcode.v2.js"></script>
<script>
    function sample6_execDaumPostcode() {
        new daum.Postcode({
            oncomplete: function(data) {
                // 팝업에서 검색결과 항목을 클릭했을때 실행할 코드를 작성하는 부분.

                // 각 주소의 노출 규칙에 따라 주소를 조합한다.
                // 내려오는 변수가 값이 없는 경우엔 공백('')값을 가지므로, 이를 참고하여 분기 한다.
                var addr = ''; // 주소 변수
                var extraAddr = ''; // 참고항목 변수

                //사용자가 선택한 주소 타입에 따라 해당 주소 값을 가져온다.
                if (data.userSelectedType === 'R') { // 사용자가 도로명 주소를 선택했을 경우
                    addr = data.roadAddress;
                } else { // 사용자가 지번 주소를 선택했을 경우(J)
                    addr = data.jibunAddress;
                }

                // 사용자가 선택한 주소가 도로명 타입일때 참고항목을 조합한다.
                if(data.userSelectedType === 'R'){
                    // 법정동명이 있을 경우 추가한다. (법정리는 제외)
                    // 법정동의 경우 마지막 문자가 "동/로/가"로 끝난다.
                    if(data.bname !== '' && /[동|로|가]$/g.test(data.bname)){
                        extraAddr += data.bname;
                    }
                    // 건물명이 있고, 공동주택일 경우 추가한다.
                    if(data.buildingName !== '' && data.apartment === 'Y'){
                        extraAddr += (extraAddr !== '' ? ', ' + data.buildingName : data.buildingName);
                    }
                    // 표시할 참고항목이 있을 경우, 괄호까지 추가한 최종 문자열을 만든다.
                    if(extraAddr !== ''){
                        extraAddr = ' (' + extraAddr + ')';
                    }
                    // 조합된 참고항목을 해당 필드에 넣는다.
                    document.getElementById("sample6_extraAddress").value = extraAddr;
                
                } else {
                    document.getElementById("sample6_extraAddress").value = '';
                }

                // 우편번호와 주소 정보를 해당 필드에 넣는다.
                document.getElementById('sample6_postcode').value = data.zonecode;
                document.getElementById("sample6_address").value = addr;
                // 커서를 상세주소 필드로 이동한다.
                document.getElementById("sample6_detailAddress").focus();
            }
        }).open();
    }
</script>

카카오는 참 친절하다. 

 

혹시 모를까 변수명까지 다 정해주고 html도 다 붙혀서 준다. 

이거 그대로 복사해서 html은 html대로 

js는 js대로 붙혀 넣어 주면 끝난다. 

 

정말 간단하지 않을가.. 

 

😻I Love kakao 😻

 

 


https://github.com/MoonSeokHyun/spring/blob/master/MyWeb/src/main/webapp/WEB-INF/views/user/userJoin.jsp

 

GitHub - MoonSeokHyun/spring

Contribute to MoonSeokHyun/spring development by creating an account on GitHub.

github.com

 

적용한 코드이다. 한번 참조 해보길~

반응형

'Web 개발' 카테고리의 다른 글

TypeScript 기본 문법(타입스크립트)  (0) 2023.04.15
인텔리제이 얼티메이트 학생 인증 무료사용 방법  (0) 2022.06.01
(JQuery) 선택자 및 속성  (0) 2022.02.04
(JQuery) 선택자  (0) 2022.02.04
(JQuery ) 적용 방법  (0) 2022.02.04
반응형

저번에 포스팅 했던 회원가입 및 로그인 검증은 우선 끝낫다. 

이제 저번에 만들었던 게시판을 한번 만들어 보고자 한다! 

 

사용 기술 Tools
Java 11 jdk STS3
Oracle 18g SQLDev

 

 

 

1. SQL 테이블 생성 😎 

 

 -- 게시판 테이블 생성
CREATE TABLE mvc_board (
    board_no NUMBER PRIMARY KEY,
    title VARCHAR2(100) NOT NULL,
    content VARCHAR2(300) NOT NULL,
    writer VARCHAR2(50) NOT NULL,
    reg_date DATE DEFAULT sysdate,
    view_cnt NUMBER DEFAULT 0
);

-- board_no에 대한 시퀀스 설정
CREATE SEQUENCE board_seq
    START WITH 1
    INCREMENT BY 1
    MAXVALUE 1000
    NOCYCLE
    NOCACHE;

우선 게시판을 만들 수있는 뼈대 즉 테이블 부터 생성해준다! 

 

 


2. BoardVO 생성 🤪

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class BoardVO {
	
	private int boardNo;
	private String title;
	private String content;
	private String writer;
	private Timestamp regDate;
	private int viewCnt;
	
	//new 마크 부착 여부 논리타입 필드
	private boolean newMark;
	

}

 

우선 Getter or Setter를 좀 더 쉽게 하기 위해 lombok을 사용하였다. 


3.  HTML 제작

 

	<div class="container">
		<div class="row">
			<div class="col-lg-2">
			</div>
			<div class="col-lg-8">
				<div class="panel-body">
				<h2 class="page-header"><span style="color: #643691;">Spring</span> 자유 게시판
					<span id="count-per-page" style="float: right;">
	                     <input class="btn btn-cpp" type="button" value="10">  
	                     <input class="btn btn-cpp" type="button" value="20">   
	                     <input class="btn btn-cpp" type="button" value="30">
                     </span>
					
				</h2>
					<table class="table table-bordered table-hover">
						<thead>
							<tr style="background-color: #643691; margin-top: 0; height: 40px; color: white; border: 0px solid #f78f24; opacity: 0.8">
								<th>#번호</th>
								<th>작성자</th>
								<th>제목</th>
								<th>작성일</th>
								<th>조회수</th>
							</tr>
						</thead>

						<!-- 게시물이 들어갈 공간 -->
						<c:forEach var="b" items="${articles}">
							<tr style="color: #643691;">
								<td>${b.boardNo}</td>
								<td>${b.writer}</td>

								<td><a style="margin-top: 0; height: 40px; color: orange;"
								 href="<c:url value="/board/content/${b.boardNo}${pc.makeUri(pc.paging.page)}"/>">
										${b.title} 
									</a>
									&nbsp;
									<c:if test="${b.newMark}">
										<img alt="newmark" src="C:\Users\mls00\OneDrive\바탕 화면\SpringWebMvcProject\src\main\webapp\resources\img\icon_new.gif">
									</c:if>
								</td>

								<td> <fmt:formatDate pattern="yyyy-MM-dd hh:mm:ss" value="${b.regDate}"/></td>
								<td>${b.viewCnt}</td>
							</tr>
						</c:forEach>
						
						
					</table>
					
					<!-- 페이징 처리 부분  -->
						<ul class="pagination justify-content-center">
					<c:if test="${pc.prev}">
                       	<li class="page-item">
							<a class="page-link" href="/board/list/?${pc.makeUri(pc.beginPage-1)}"
							style="background-color: #643691; margin-top: 0; height: 40px; color: white; border: 0px solid #f78f24; opacity: 0.8">이전</a>
						</li></c:if>
				
						<c:forEach var="num" begin="${pc.beginPage}" end="${pc.endPage}" >
							<li class="page-item">
						   <a href="/board/list${pc.makeUri(num)}" 
						   class="page-link  ${pc.paging.page == num ? 'page-active': '' }" style="margin-top: 0; height: 40px; color: pink; border: 1px solid #643691;">${num}</a>
						</li>
					   </c:forEach>
					
					 <c:if test="${pc.next}">
					     <li class="page-item">
					      <a class="page-link" href='<c:url value='/board/list${pc.makeUri(pc.endPage+1)}}'/>' 
					      style="background-color: #643691; margin-top: 0; height: 40px; color: white; border: 0px solid #f78f24; opacity: 0.8">다음</a>
					    </li>
				    </ul></c:if>
					
					<!-- 페이징 처리 끝 -->
					</div>
				</div>
			</div>
					<!-- 검색 버튼 -->
					<div class="row">
						<div class="col-sm-2"></div>
	                    <div class="form-group col-sm-2">
	                        <select id="condition" class="form-control" name="condition">                            	
	                            <option value="title" ${param.condition == 'title' ? 'selected' : '' }>제목</option>
	                            <option value="content"${param.condition == 'content' ? 'selected' : '' }>내용</option>
	                            <option value="writer"${param.condition == 'writer' ? 'selected' : '' }>작성자</option>
	                            <option value="titleContent"${param.condition == 'titleContent' ? 'selected' : '' }>제목+내용</option>
	                        </select>
	                    </div>
	                    <div class="form-group col-sm-4">
	                        <div class="input-group">
	                            <input type="text" class="form-control" name="keyword" id="keywordInput" placeholder="검색어" value="${param.keyword}">
	                            <span class="input-group-btn">
	                                <input type="button" value="검색" class="btn btn-cpp btn-flat" id="searchBtn">                                       
	                            </span>
	                        </div>
	                    </div>
	                    <div class="col-sm-2">
							<a href="<c:url value ="/board/write"/>" class="btn btn-cpp float-right">글쓰기</a>
						</div>
						<div class="col-sm-2"></div>
					</div>
		
	</div>

 

기본 뼈대의 Html이다 CSS는 BOOTSTRAP을 사용하였다. 😎😎😎

 


4. 본격적인 Spring 시작!😁

 

4-1 Controller 제작

 

 

@Controller
@RequestMapping("/board")
public class BoardController {

   @GetMapping("/list")
   public String list(Model model) {
      System.out.println("/board/list: GET");
      model.addAttribute("articles", service.getArticleList());
      
      return "board/list";
   	}
   }

 

 

4-2 인터페이스 제작

package com.spring.mvc.board.service;

import java.util.List;

import com.spring.mvc.board.commons.SearchVO;
import com.spring.mvc.board.commons.pageVO;
import com.spring.mvc.board.model.BoardVO;

public interface IBoardService {

	//게시글 등록 기능
	void insert(BoardVO article);

	//게시글 전체 목록 기능
	List<BoardVO> getArticleList(SearchVO search);

	//게시글 상세 조회 기능
	BoardVO getArticle(int boardNo);

	//게시글 수정 기능
	void update(BoardVO article);

	//게시글 삭제 기능
	void delete(int boardNo);

	//게시물 수 조회 기능 
	int countArticles(SearchVO search);
	
	//날자 한글로 변경
	
}

 

IboardService를 만든다. 기능은 단순하게 세팅해준다.

 

그 다음은 IBoardMapper를 생성해준다.

 

package com.spring.mvc.board.repository;

import java.util.List;
import java.util.Map;

import org.apache.ibatis.annotations.Param;

import com.spring.mvc.board.commons.SearchVO;
import com.spring.mvc.board.commons.pageVO;
import com.spring.mvc.board.model.BoardVO;

public interface IBoardMapper {

	//게시글 등록 기능
	void insert(BoardVO article);
	
	//검색 결과와 페이지 정보까지 가지고 있는 하나의 객체를 매개값으로 받는 방식
	List<BoardVO> getArticleList(SearchVO search);
	//게시글 상세 조회 기능
	BoardVO getArticle(int boardNo);
	
	//게시글 수정 기능
	void update(BoardVO article);
	
	//게시글 삭제 기능
	void delete(int boardNo);
	
	//게시물 수 조회 기능
	int countArticles(SearchVO search);


	
	
}

 여기서 주의 할 점!

 

 MYBatis로 db연동을 진행할 떄 파라미터 값이 2개 이상이라면 
 1. @param으로 작성하는법

 2. map으로 포장해서 보내는법 

 3. 객체하나를 매개값으로 보내는법 들을 적절하게 상황에 맞게 선택하면 됨

 

 

주로 Map을 이용한 방법을 많이 쓴다. 

 

 


5. Service 제작 👏

package com.spring.mvc.board.service;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.spring.mvc.board.commons.SearchVO;
import com.spring.mvc.board.commons.pageVO;
import com.spring.mvc.board.model.BoardVO;
import com.spring.mvc.board.repository.IBoardMapper;

@Service
public class BoardService implements IBoardService {
	
	@Autowired
	private IBoardMapper mapper;

	@Override
	public void insert(BoardVO article) {
		mapper.insert(article);
	}


	@Override
	public List<BoardVO> getArticleList(SearchVO search) {
		//mapper에게 전달할 맵 데이터를 생성
//		Map<String, Object> datas = new HashMap<>();
//		datas.put("paging", paging);
//		datas.put("keyword", keyword);
//		datas.put("condition", condition);
		
		List<BoardVO> list = mapper.getArticleList(search);
		for(BoardVO article : list) {
			long now = System.currentTimeMillis();
			long regTime = article.getRegDate().getTime();
			if(now - regTime < 60 * 60*24 *1000) {
				article.setNewMark(true);
			}
		}
		return list;
	}

	@Override
	public BoardVO getArticle(int boardNo) {
		return mapper.getArticle(boardNo);
	}

	@Override
	public void update(BoardVO article) {
		mapper.update(article);
	}

	@Override
	public void delete(int boardNo) {
		mapper.delete(boardNo);
	}
	
	@Override
	public int countArticles(SearchVO search){
		return mapper.countArticles(search);
	}
	
}

 

IServiceBoard를 Add하여 생성해준다.

 

 

 


6. Mapper 제작🤞

 

Mapper 를 제작해준다. 

이때 꿀팁

<mybatis-spring:scan base-package="com.spring.mvc.board.repository"/>
<mybatis-spring:scan base-package="com.spring.mvc.user.repository"/>

Context-root.xml 파일일에 Mybatis.xml파일을 bean으로 등록하기 위한 스캔 설정을 해줍니다. 

<interceptors>
		<interceptor>
			<!-- <mapping path="/board/**"/>  -->	
			<mapping path="/board/write"/>
			<mapping path="/board/content/**"/> <!-- **은 글번호  -->
			<beans:bean class="com.spring.mvc.board.commons.intercertor.BoardInterceptor" />
		</interceptor>

이때 servlet-config.xml에서 이렇게 설정을 해주면 별도로 xml 파일을 만들때 빈등록을 안해줘도 됩니다. 

👏👏👏👏즉 위 지정된 경로만 지정해주면 된다! 👏👏👏👏

 

위에 지정한 대로 파일을 이렇게 설정 하였다!

	<select id="getArticleList" resultMap="BoardMap">

		SELECT * FROM mvc_board
		
		ORDER BY board_no DESC
		

	</select>

리스트를 불러올 수 있도록 Select 값을 넣어준다!

	<insert id="insert">
		INSERT INTO mvc_board
		(board_no, title, content, writer)
		VALUES(board_seq.NEXTVAL,#{title},#{content},#{writer})
	</insert>

그리고 현재 insert 관련하여 컨트롤러나 이런부분을 만들지 않았다 하지만 게시물이 보이는

리스트를 만들려면 insert를 하여 볼 데이터가 필요했다. 

sqldev로 해도 되지만 plsql하기 귀찮아 Junit으로 테스트를 한다. 

 


7. Junit Test

 

package com.spring.mvc.board;

import java.util.List;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.spring.mvc.board.model.BoardVO;
import com.spring.mvc.board.repository.IBoardMapper;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"file:src/main/webapp/WEB-INF/spring/mvc-config.xml"})
public class BoardMapperTest {

	@Autowired
	private IBoardMapper mapper;
	
	//게시글 등록 단위 테스트
	@Test
	public void insertTest() {
		for(int i=1; i<=300; i++) {
			BoardVO article = new BoardVO();
			article.setTitle("테스트 제목입니다." + i);
			article.setWriter("김테스트" + i);
			article.setContent("테스트 중이니까 조용히 하세요!" + i);
			mapper.insert(article);
		}
	}

 

우선 300개만 가볍게 넣어보자

 

데이터가 잘 들어갔다면 이제 화면에 뿌려주자!

 


8. 화면 작업

<c:forEach var="b" items="${articles}">
		<tr style="color: #643691;">
		<td>${b.boardNo}</td>
		<td>${b.writer}</td>

		<td><a style="margin-top: 0; height: 40px; color: orange;"
		href="<c:url value="/board/content/${b.boardNo}${pc.makeUri(pc.paging.page)}"/>">
		${b.title} 
		</a>
		&nbsp;
		<c:if test="${b.newMark}">
		<img alt="newmark" src="C:\Users\mls00\OneDrive\바탕 화면\SpringWebMvcProject\src\main\webapp\resources\img\icon_new.gif">
		</c:if>
		</td>

		<td> <fmt:formatDate pattern="yyyy-MM-dd hh:mm:ss" value="${b.regDate}"/></td>
		<td>${b.viewCnt}</td>
		</tr>
		</c:forEach>
						
						
		</table>

 

Jstl 을 이용하여 받아온 파라미터 값을 뿌려준다.

😎<c:forEach var="b" items="${articles}">😎

😎변수는 b로 하고 뿌려준다!😎


😎

9. 동작 확인🙏

 

 

테스트로 넣은 게시글이 잘 보이는걸 알 수 있다!

 

또 오류 나는 줄 알고 십년감수했다.☠️

 

 

 

주인장 Github 

 

https://github.com/MoonSeokHyun

반응형
반응형

저번에 포스팅 했던 회원가입 진행 및 아이디 중복 체크에 404에러 뜨던것 해결 했다. 

 

나는 바보였다.  학원 강사님께 문의 하니 경로가 잘 못되었엇다. 

 

Spring

🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏

기도 메타가 부족했나 이번에는 🙏 *100으로  도전 한다. 

 


1. 문제의 원인🕵️‍♂️

 

<Context docBase="MyWeb" path="/myweb" reloadable="true" 
source="org.eclipse.jst.jee.server:MyWeb"/><Context docBase="SpringWebMvcProject"
path="/mvc" 
reloadable="true" source="org.eclipse.jst.jee.server:SpringWebMvcProject"/></Host>
    </Engine>

톰캣 서버 내의 context root의 path가 /인줄 알았는데 알고보니 /mvc 더라..

 


 

2. 문제의 해결🕵️‍♂️

 

$.ajax({
         		 type :'post', // 서버에 전송하는 http방식
         		 url :'/mvc/user/checkId', // 서버 요청 url
         		 headers : {
         			 'Content-Type' : 'application/json'
         		 },
        		 data : id, // 서버로 전송할 데이터 // 위에서 지정한 const id 
        		 success : function(result) { // 매개변수에 통신성공시 데이터가 저장된다.
					//서버와 통신성공시 실행할 내용 작성.
					console.log('통신 성공!' + result);
        		 	if(result === 'available'){
        		 		 $('#user_id').css('background-color', 'aqua');
        		 		 $('#idChk').html('<b style="font-size: 14px; color: green">[아이디 사용이 가능하다.]</b>');
        		 		 chk1 = true;
        		 	}else{
        		 		 $('#user_id').css('background-color', 'pink');
        		 		 $('#idChk').html('<b style="font-size: 14px; color: red">[아이디 중복!.]</b>');
        		 		 chk1 = false;
        		 	}
				},
				error : function (status, error) { //통신에 실패했을때
					console.log('통신실패');
					console.log(status, error)
				}
          	}); // end ajax(아이디 중복 확인)
         }

url 부분은 기존에 /mvc/가 빠졋었다. 

/mvc를 넣었더니 정상 작동을 하였다. 

 

 

이것과 별개로 

 

url : "<c:url value ='/user/checkId'/>

 

<c:url value ='url값'/>을 쓰는것도 방법이다. 다음부터는 이방법 위주로 쓰려고 한다.,😻😻😻

 

 


3. 정상 동작 확인💪

드디어 정상적으로 확인!!

 

드디어 올 파랭이를 볼수 있음!!!

 

👏👏👏👏👏👏👏👏

 

 

문제 해결 !👏

 

 

주인장  git 주소 : https://github.com/MoonSeokHyun

반응형
반응형
	@Override
	public void keepLogin(String session, Date limitTime, String account) {
		Map<String, Object> datas = new HashMap<String, Object>();
		datas.put("sessionId", session);
		datas.put("limitTime", limitTime);
		datas.put("account", account);
		
		mapper.keepLogin(datas);
	}
    
    	@Override
	public UserVO selectOne(String account) {
		return mapper.selectOne(account);
	}

로그인 처리를 위한 html 코드 작성. 🤩

 

부트스트랩으로 작성되었음!!

<div class="modal-body">
<table style="cellpadding: 0; cellspacing: 0; margin: 0 auto; width: 100%">
<tr>
	<td style="text-align: left">
		<p><strong>아이디를 입력해주세요.</strong>&nbsp;&nbsp;&nbsp;<span id="idCheck"></span></p>
	</td>
</tr>
<tr>
<td><input type="text" name="userId" id="signInId"
	class="form-control tooltipstered" maxlength="10"
	required="required" aria-required="true"
	style="margin-bottom: 25px; width: 100%; height: 40px; border: 1px solid #d9d9de"
	placeholder="최대 10자"></td>
	</tr>
	<tr>
<td style="text-align: left">
<p><strong>비밀번호를 입력해주세요.</strong>&nbsp;&nbsp;&nbsp;<span id="pwCheck"></span></p>
</td>
/tr>
	<tr>
	<td><input type="password" size="17" maxlength="20" id="signInPw"
	name="userPw" class="form-control tooltipstered" 
	maxlength="20" required="required" aria-required="true"
	style="ime-mode: inactive; margin-bottom: 25px; height: 40px; border: 1px solid #d9d9de"
								placeholder="최소 8자"></td>
		</tr>
		<!--  자동로그인 체크박스 -->
		<tr>
		<td><input type="checkbox" id="auto-login" name="autoLogin"> 자동 로그인</td>
			</tr>
			<tr>
		td style="padding-top: 10px; text-align: center">
		<p><strong>로그인하셔서 더 많은 서비스를 이용하세요~</strong></p>
			</td>
			</tr>
		tr>
		td style="width: 100%; text-align: center; colspan: 2;"><input
		type="button" value="로그인" class="btn form-control tooltipstered" id="signIn-btn"
		style="background-color: #643691; margin-top: 0; height: 40px; color: white; border: 0px solid #f78f24; opacity: 0.8">
			</td>
			</tr>
				<tr>
				<td
style="width: 100%; text-align: center; colspan: 2; margin-top: 24px; padding-top: 12px; border-top: 1px solid #ececec">

<a class="btn form-control tooltipstered" data-toggle="modal"
href="#sign-up"
style="cursor: pointer; margin-top: 0; height: 40px; color: white; background-color: orange; border: 0px solid #388E3C; opacity: 0.8">
			회원가입</a>
			</td>
			</tr>

		</table>
			
			</div>
		</div>
	</div>
</div>

2. 로그인 처리를 위한 JQuery문 🤩

  - 아이디 검증

 

 //각 입력값들의 유효성 검증을 위한 정규표현식을 변수로 선언.
         const getIdCheck = RegExp(/^[a-zA-Z0-9]{4,14}$/);
         const getPwCheck = RegExp(/([a-zA-Z0-9].*[!,@,#,$,%,^,&,*,?,_,~])|([!,@,#,$,%,^,&,*,?,_,~].*[a-zA-Z0-9])/);
         const getNameCheck = RegExp(/^[가-힣]+$/);
// ID 입력값 검증 (공백확인 , 정규표현식 어긋나는지 확인)
		$('#signInId').keyup(function() {
			if($(this).val() === ''){
				$(this).css('background-color', 'pink');
			 	$('#idCheck').html('<b style="font-size: 14px; color: red">[아이디는 필수 값입니다.]</b>');
			 	chk1 = false;
			}else if(!getIdCheck.test($(this).val())){
				$(this).css('background-color', 'pink');
			 	$('#idCheck').html('<b style="font-size: 14px; color: red">[아이디가 형식에 어긋납니다!]</b>');
			 	chk1 = false;
			}else{
				$(this).css('background-color', 'aqua');
			 	$('#idCheck').html('<b style="font-size: 14px; color: red">[아이디 입력 성공!]</b>');
			 	chk1 = true;
			}
/ 비밀번호 입력값 검증 (공백확인 , 정규표현식 어긋나는지 확인)
		$('#signInPw').keyup(function() {
			if($(this).val() === ''){
				$(this).css('background-color', 'pink');
			 	$('#pwCheck').html('<b style="font-size: 14px; color: red">[비밀번호는 필수 값입니다.]</b>');
			 	chk2 = false;
			}else if(!getPwCheck.test($(this).val())){
				$(this).css('background-color', 'pink');
			 	$('#pwCheck').html('<b style="font-size: 14px; color: red">[비밀번호가 형식에 어긋납니다.]</b>');
			 	chk2 = false;
			}else{
				$(this).css('background-color', 'aqua');
			 	$('#pwCheck').html('<b style="font-size: 14px; color: red">[비밀번호 입력 성공!]</b>');
			 	chk2 = true;
			}
		}); // 비밀번호 검증 끝

 

 

정규식 표현식으로 아이디가 어긋나는지 or 빈칸인지 둘다 만족 된다면 

else값으로 이동하여 OK표기!

 

 

$('#signIn-btn').click(function() {
			if(chk1 && chk2){
				// ajax를 이용한 비동기 방식으로 로그인을 처리할 예정이다. 
		        /*
		        아이디, 비밀번호를 가져오셔서 객체로 포장하세요.
		        비동기 통신을 진행하여 서버로 객체를 json형태로 전송하세요.
		        그리고, console.log()로 서버가 보내온 데이터를 확인하여
		        아이디가 없습니다, 비밀번호가 틀렸습니다, 로그인 성공이라는
		        메세지를 브라우저의 console창에서 확인하세요.
		        서버에서 클라이언트로 데이터 전송은 text로 이루어 질 것이며
		        idFail, pwFail, loginSuccess라는 문자열을 리턴할 것입니다.
		        전송방식: POST, url: /user/loginCheck
		        */
		        
				const id = $('#signInId').val();
				const pw = $('#signInPw').val();
				
				//자동로그인 체크박스가 체크가 되었는지의 여부 
				//제이쿼리 is 상태여부를 확인할 수 있는 함수 논리값을 판단하여 논리값을 리턴
				const autoLogin = $('#auto-login').is(':checked');
				
				console.log("id" + id);
				console.log("pw" + pw);
				const userInfo ={
						"account" : id,
						"password" : pw,
						
						//오토로그인 추가 체크의 여부를 확인하여 자동로그인을 구현할지 말지를 정한다. 
						"autoLogin" : autoLogin
				};
				// ajax 시작!
				$.ajax({
					type : "post",
					url : "/user/loginCheck",
					contentType : "application/json",
					dataType : "text",
					data : JSON.stringify(userInfo),
					success : function(result) {
						console.log("통신 성공" + result);
						if(result === 'idFail'){
							//console.log('아이디가 없습니다.');
							$('#idCheck').css('background-color', 'pink');
						 	$('#idCheck').html('<b style="font-size: 14px; color: red">[아이디가 없습니다.]</b>');
						 	$('#signInPw').val('');
						 	$("#singInPw").focus(); // 커서 이동 및 스크롤도 해당 위치로 이동시키는 함수 
						 	 chk2 = false;
						 	
						}else if (result === 'pwFail') {
							//console.log('비밀번호가 틀렸습니다.');
							$('#pwCheck').css('background-color', 'pink');
						 	$('#pwCheck').html('<b style="font-size: 14px; color: red">[비밀번호가 틀렷습니다.]</b>');
						 	$('#signInPw').val('');
						 	$("#singInId").focus(); // 커서 이동 및 스크롤도 해당 위치로 이동시키는 함수 
						 	chk1 = false;  chk2 = false;
						}else{
							console.log('로그인 성공');
							location.href = '/';
						}
					},
					error : function() {
						alert('로그인 실패!');
					}
				}); // ajax 종료!
				
			}else{
				alert('입력값을 다시 확인하세요');
			}
		}); /

if(chk1 && chk2){ 의값으로 

아이디 및 비밀번호 입력값이 모두가 true값이 진행되면 로그인 이벤트 처리 

ajax(비동기 통신으로 진행) 이제 컨트롤러 작성하러 가보자! 😀

 


3. 스프링 작성 🤩 

 

 

자 이제 컨트롤러 및 서비스 > 매퍼 및 xml을 작성 해보자!!

 

이번에도 비동기 통신임으로 

@RestController 
@RequestMapping("/user")

를 진행해준다. 그리고 user라는 url로 진행하였다. 

 

ajax에서 loginCheck라는 url으로 컨트롤러에 요청을 보내니 loginCheck와 같은 이름으로 작성한다. 

 

@PostMapping("/loginCheck")
	public String loginCheck(@RequestBody UserVO vo, HttpSession session , HttpServletResponse respones  ) {//HttpServletRequest request) {
		System.out.println("/user/logincheck : post");
		System.out.println("param : " + vo);
		
		// 매개값으로 httpsession 객체 받아서 사용
		BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
		UserVO dbData =  serivce.selectOne(vo.getAccount());
		// mybatis는 조회된 데이터가 없을경우 null이 온다.
		if(dbData != null) {
			if(encoder.matches(vo.getPassword(), dbData.getPassword())) {
				//로그인 성공 히원을 대상으로 세션 정보를 생성
				session.setAttribute("login", dbData);
					serivce.keepLogin(session.getId(), limitDate, vo.getAccount());
				}
				return "loginSuccess";
			}else {
				return "pwFail";
			}
		}else {
			return "idFail";
		}
		
	}

이때 session.setAttribute("login", dbData); 세션을 생성해준다. 

스프링에서는 간단하게 세션을 만들수 있다. 라는 login이라는 이름으로 dbDate의 값을 만들었다 . 이곳에는 userVO의 객체의 값이 들어있다. 

 

이때 selectOne의 서비스와 keeplogin의 코드를 작성한다. 

 

 

	@Override
	public UserVO selectOne(String account) {
		return mapper.selectOne(account);
	}
    
    	@Override
	public void keepLogin(String session, Date limitTime, String account) {
		Map<String, Object> datas = new HashMap<String, Object>();
		datas.put("sessionId", session);
		datas.put("limitTime", limitTime);
		datas.put("account", account);
		
		mapper.keepLogin(datas);
	}

 

이때 SeletOne은 로그인하는 아이디의 검증을 위해 사용된다. 

keeplogin은 세션의 유지 등에 사용된다. 

 

package com.spring.mvc.user.repository;

import java.util.Map;

import com.spring.mvc.user.model.UserVO;

public interface IUserMapper {
	
	//아이디 중복체크 기능
	int checkId(String account);
	
	//회원 가입기능
	void register(UserVO vo);
	
	//회원 정보 조회 기능 
	UserVO selectOne(String account);
	
	//회원 탈퇴 기능 
	void delete(String account);
	
	//자동 로그인 쿠키값 db 저장 처리
	//sql > update 문으로 작성
	void keepLogin(Map<String, Object> datas);
	
	//세션아이디를 통한 회원 정보조회기능
	/*
	  - 자동로그인을 하고 싶다는 사람한테 뭘 만들어 줬죠? > 쿠키(세션Id)
	  그리고 나서 그 사람이 나중에 우리사이트에 다시 방문했다고 칩시다. 
	  단연히 우리 서버에 요청을 보낼 거고 , 요청과 함게 쿠키도 같이 전달이 되겠죠? 
	  우리는 쿠키 안에 들어있는 세션 id로 회원 정보를 조회해서 마치 이사람이 로그인 중인 것 처럼 
	  세션 데이터를 만들어 주자는 겁니다. (login이라는 세션 데이터 -> 로그인 중이라는 징표)
	 */
	UserVO getUserWithSessionId(String sessionId);
	
	
}

 

매퍼 작성 후 xml을 작성해준다. 

 

	<!-- 자동로그인을 희망하는 경우 쿠키값(세션아이디)와 유효시간을 갱신 -->
	<update id="keepLogin">
		update mvc_user set session_id = #{sessionId}, LIMIT_TIME = #{limitTime}
		where account = #{account}
	</update>
    
         <select id="selectOne" resultMap="UserMap">
		SELECT * FROM mvc_user
		WHERE account=#{account}
	</select>

 

 

이제 세션이 얻어 졌으니 로그인 유지 작업을 해보자!

 


세션의 활용 로그인 유지 HTML,JS 작업😀

 

  <c:if test="${login != null}">
	           <li class="nav-item">
	            <a class="nav-link js-scroll-trigger" href="#">My Page</a>
	          </li>
	           <li class="nav-item">
	            <a class="nav-link js-scroll-trigger" href="<c:url value='/user/logout'/>" onclick="confirm('로그아웃 하시겠음?')">LOGOUT</a>
	          </li>
	          
           </c:if>
          
          <c:if test="${login == null}">
	           <li class="nav-item">
	            <a class="nav-link js-scroll-trigger" data-toggle="modal" data-target="#log-in">LOGIN</a>
	          </li>
  </c:if>

 

jstl을 활용하여 로그인 전에는 로그인 버튼이 생성되게

로그인 후에는 로그아웃 및 마이페이지가 생성  되게 하였다. 

 

 


마무리 확인 작업 😁

🙏

🙏

🙏

🙏

🙏

🙏

🙏

자!! 기도 메타 한번 가고

 

로그인 후의 navbar 화면
로그인 하기 전의 화면 

 

이렇게 세션 적용은 완료되었다. !

 

 

chk 1 && chk2 가 모두 true일 경우
둘중 하나가 false인 경우 로그인이 안된다.
모두 false일 경우

반응형
반응형

Js.html 영역

 

 

1. 기존의 회원가입 jsp부분의 HTML 파일

<div class="modal fade" id="sign-up" role="dialog">
	<div class="modal-dialog">

		<!-- Modal content-->
		<div class="modal-content">
			<div class="modal-header">
				<h4 class="modal-title">
					<span style="color: #643691;">Spring</span> 회원 가입
				</h4>
				<button type="button" class="close" data-dismiss="modal">×</button>

			</div>

			<div class="modal-body">


				<table
					style="cellpadding: 0; cellspacing: 0; margin: 0 auto; width: 100%">
					<tr>
						<td style="text-align: left">
							<p>
								<strong>아이디를 입력해주세요.</strong>&nbsp;&nbsp;&nbsp;<span id="idChk"></span>
							</p>
						</td>


					</tr>
					<tr>
						<td><input type="text" name="userId" id="user_id"
							class="form-control tooltipstered" maxlength="14"
							required="required" aria-required="true"
							style="margin-bottom: 25px; width: 100%; height: 40px; border: 1px solid #d9d9de"
							placeholder="숫자와 영어로 4-10자"></td>

					</tr>

					<tr>
						<td style="text-align: left">
							<p>
								<strong>비밀번호를 입력해주세요.</strong>&nbsp;&nbsp;&nbsp;<span id="pwChk"></span>
							</p>
						</td>
					</tr>
					<tr>
						<td><input type="password" size="17" maxlength="20"
							id="password" name="userPw" class="form-control tooltipstered"
							maxlength="20" required="required" aria-required="true"
							style="ime-mode: inactive; margin-bottom: 25px; height: 40px; border: 1px solid #d9d9de"
							placeholder="영문과 특수문자를 포함한 최소 8자"></td>
					</tr>
					<tr>
						<td style="text-align: left">
							<p>
								<strong>비밀번호를 재확인해주세요.</strong>&nbsp;&nbsp;&nbsp;<span
									id="pwChk2"></span>
							</p>
						</td>
					</tr>
					<tr>
						<td><input type="password" size="17" maxlength="20"
							id="password_check" name="pw_check"
							class="form-control tooltipstered" maxlength="20"
							required="required" aria-required="true"
							style="ime-mode: inactive; margin-bottom: 25px; height: 40px; border: 1px solid #d9d9de"
							placeholder="비밀번호가 일치해야합니다."></td>
					</tr>

					<tr>
						<td style="text-align: left">
							<p>
								<strong>이름을 입력해주세요.</strong>&nbsp;&nbsp;&nbsp;<span id="nameChk"></span>
							</p>
						</td>
					</tr>
					<tr>
						<td><input type="text" name="userName" id="user_name"
							class="form-control tooltipstered" maxlength="6"
							required="required" aria-required="true"
							style="margin-bottom: 25px; width: 100%; height: 40px; border: 1px solid #d9d9de"
							placeholder="한글로 최대 6자"></td>
					</tr>

					<tr>
						<td style="padding-top: 10px; text-align: center">
							<p>
								<strong>회원가입을 환영합니다~~!</strong>
							</p>
						</td>
					</tr>
					<tr>
						<td style="width: 100%; text-align: center; colspan: 2;"><input
							type="button" value="회원가입" class="btn form-control tooltipstered"
							id="signup-btn"
							style="background-color: #643691; margin-top: 0; height: 40px; color: white; border: 0px solid #388E3C; opacity: 0.8">
						</td>
					</tr>

				</table>

			</div>
		</div>
	</div>
</div>

</div>

 

 

2. 패스워드 입력값 검증을 위한 Js코드

 

 

         const getIdCheck = RegExp(/^[a-zA-Z0-9]{4,14}$/);
         const getPwCheck = RegExp(/([a-zA-Z0-9].*[!,@,#,$,%,^,&,*,?,_,~])|([!,@,#,$,%,^,&,*,?,_,~].*[a-zA-Z0-9])/);
         const getNameCheck = RegExp(/^[가-힣]+$/);
         
         
          $('#password').keyup(function() {
		
    	  //비밀번호 란에 공백 확인 
    	  if($(this).val() === ''){
    		  $(this).css('background-color', 'pink');
		 	  $('#pwChk').html('<b style="font-size: 14px; color: red">[비밀번호는 필수 정보 입니다..]</b>');
		 	  chk2 = false;
    	  }
    	  
    	  //비밀번호 유효성 검사 
    	  else if(!getPwCheck.test($(this).val()) || $(this).val().length < 8){
    		  $(this).css('background-color', 'pink');
		 	  $('#pwChk').html('<b style="font-size: 14px; color: red">[특수 문자 포함 8글자 이상 !]</b>');
		 	 chk2 = false;
    	  }// 통과
    	  else{
    		  $(this).css('background-color', 'aqua');
		 	  $('#pwChk').html('<b style="font-size: 14px; color: green">[비밀번호 입력 완료!]</b>');
		 	 chk2 = true;
    	  }
    	  
	});

 

위의 const getpwcheck로 코드로 비밀번호의 유효성 검사를 한다.

 

3. 비밀번호 확인란 검증

 

//3. 비밀번호 확인란 입력 검증 
$('#password_check').keyup(function() {
// 비밀번호 확인란 검증 
if($(this).val() === ''){
	$(this).css('background-color', 'pink');
	$('#pwChk2').html('<b style="font-size: 14px; color: red">[비밀번호는 확인은 필수 정보 입니다.]</b>');
	chk3 = false;
	}
	/ 비밀번호 확인란 유효성 검사.
	else if($(this).val() != $('#password').val()){
	 $(this).css('background-color', 'pink');
	 $('#pwChk2').html('<b style="font-size: 14px; color: red">[비밀번호가 일치하지 않습니다.]</b>');
	 chk3 = false;
	}else{
	 $(this).css('background-color', 'aqua');
	 $('#pwChk2').html('<b style="font-size: 14px; color: green">[비밀번호 입력 확인 완료]</b>');
	chk3 = true;
}

 

 

4. 이름 입력값 검증

 

		$('#user_name').keyup(function() {
			if($(this).val === ''){
				 $(this).css('background-color', 'pink');
			 	 $('#nameChk').html('<b style="font-size: 14px; color: red">[이름은 필수 정보 입니다. ]</b>');
			 	 chk4 = false;
			}else if(!getNameCheck.test($(this).val())){
				 $(this).css('background-color', 'pink');
			 	 $('#nameChk').html('<b style="font-size: 14px; color: red">[이름은 한글 이외에 다른 값은 입력 할 수 없습니다. ]</b>');
			 	 chk4 = false;
			}else{
				$(this).css('background-color', 'aqua');
			 	 $('#nameChk').html('<b style="font-size: 14px; color: red">[이름 입력 완료! ]</b>');
			 	 chk4 = true;
			}
		}); //이름 입력 끝

5. 회원가입 

 

$('#signup-btn').click(function() {
			
			if(chk1 && chk2 && chk3 && chk4){
				
				//Id정보 
				const id = $("#user_id").val();
				//pw정보
				const pw = $('#password').val();
				//name
				const name = $('#user_name').val();
				//프로퍼티 이름은 반드시 userVO와 같아야 동작이 된다!! 매우 중요!
				const user ={
						"account" : id,
						"password" : pw,
						"name" : name
						// json 아님
				}
				
				// 비동기 통신 시작! 
				$.ajax({
					type : "post",
					url : "/user/",
					contentType : "application/json",
					dataType : "text",
					// 자바스크립트 객체를 json문자열로 변환해 주는 메소드
					data : JSON.stringify(user),
					success : function(result) {
						console.log("통신 성공" + result);
						alert('회원가입을 축하합니다.');
						location.href ="/";
					},
					error : function() {
						alert('회원가입 실패!');
					}
				})
			}else {
				alert('입력정보를 확인 해 주세요.');
			}
			
		})/

사용자가 회원 가입 버튼을 눌렀을 때 이벤트 처리 및 사용자가 입력하는 4가지 데이터 

(ID, PASSWORD, PASSWORD_CHECK, NAME ) 가 모두 일치해야 된다.

 

이때 ajax에서 url user쪽 컨트롤러로 const user의 정보의 객체에 담아 json 형식으로 요청을 보내게 된다.

 


스프링 영역

 

6. 컨트롤러 작성

 

	@PostMapping("/")
	public String register(@RequestBody UserVO vo) {
		System.out.println("/user/ : post");
		serivce.register(vo);
		return "joinSuccess";
	}

 

 

ajax에서 user객체를 담아 컨트롤러로 보내주어 @RequestBody의 어노테이션으로 UserVO를 전달 받아

service 쪽으로 넘기게 된다. 

 

7. 서비스 작성

 

	public void register(UserVO vo) {
		//회원 비밀번호를 암호화 인코딩
		BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
		System.out.println("암호화 하기 전 비번 : " + vo.getPassword());
		
		//비밀번호를 암호화 해서 user객체에 다시 저장하기.
		String securepw = encoder.encode(vo.getPassword());
		System.out.println("암호화 후 비번 : " + securepw);
		vo.setPassword(securepw);
		
		mapper.register(vo);
	}

이때 스프링의 메이븐 API인 회원 비밀번호를 암호화 하여 저장한다. 

사용 Api 명은 Spring Security이다 사용방법은 나중에 포스팅 하도록 겠다. 

 

4,5번은 암호화를 진행한 패스워드 6번은 암호화 미적용

다만 주의할 점은 암호화를 할 경우에 암호화 이전에 만들었던 아이디와 패스워드로는 로그인이 불가능하다.

 

이제 서비스에서 Mapper.register 쪽으로  User의 값을 전달한다.

 

	void register(UserVO vo);

이제 회원 가입을 할 xml을 만들자

 

8. Mapper.xml 작성

 

 <mapper namespace="com.spring.mvc.user.repository.IUserMapper">
    
    <resultMap type="com.spring.mvc.user.model.UserVO" id="UserMap">
    	<result property="regDate" column="reg_date"/>
    	<result property="sessionId" column="session_id"/>
    	<result property="limitTime" column="limit_time"/>
    </resultMap>
    
     	<insert id="register">
     	iNSERT INTO mvc_user
		(account,password,name)
		VALUES(#{account},#{password},#{name})
     	</insert>

 

코드블럭에는 xml이 없구낭... 

 

mapper의 namespace에는 직전에 작성하였던 IuserMapper의 경로를 넣어준다. 

 

이때 데이터베이스의 컬럼명과 VO객체의 변수명의 이름이 다를 때 ResultMap 이라는 태그를 작성해야한다. 

property ="UserVO의 변수명" column ="DB의 컬럼명" 을 작성해 준다.

 

단 이것은 Select문에서 적용한다 

Insert에서는 사용하지 않는다 하지만  미리 작성해서 나쁠건 없다.

 

 


 

8. 최종 동작 확인 

 

ㅇ ㅏ.. ajax에서 404에러다.. 

컨트롤러에서 리퀘스트바디나 url 문제같은데.. 

 

다했는데.. 

 

다음 포스팅은 아마도 오류 해결을 해야할거같다..

반응형
반응형

1. 회원가입 html을 작성 

<!-- 회원가입 Modal -->
<div class="modal fade" id="sign-up" role="dialog">
	<div class="modal-dialog">

		<!-- Modal content-->
		<div class="modal-content">
			<div class="modal-header">
				<h4 class="modal-title">
					<span style="color: #643691;">Spring</span> 회원 가입
				</h4>
				<button type="button" class="close" data-dismiss="modal">×</button>

			</div>

			<div class="modal-body">

				
					<table
						style="cellpadding: 0; cellspacing: 0; margin: 0 auto; width: 100%">
						<tr>
							<td style="text-align: left">
								<p><strong>아이디를 입력해주세요.</strong>&nbsp;&nbsp;&nbsp;<span id="idChk"></span></p>
							</td>
								
							
						</tr>
						<tr>
							<td><input type="text" name="userId" id="user_id"
								class="form-control tooltipstered" maxlength="14"
								required="required" aria-required="true"
								style="margin-bottom: 25px; width: 100%; height: 40px; border: 1px solid #d9d9de"
								placeholder="숫자와 영어로 4-10자">
								</td>
							
						</tr>

						<tr>
							<td style="text-align: left">
								<p><strong>비밀번호를 입력해주세요.</strong>&nbsp;&nbsp;&nbsp;<span id="pwChk"></span></p>
							</td>
						</tr>
						<tr>
							<td><input type="password" size="17" maxlength="20" id="password"
								name="userPw" class="form-control tooltipstered" 
								maxlength="20" required="required" aria-required="true"
								style="ime-mode: inactive; margin-bottom: 25px; height: 40px; border: 1px solid #d9d9de"
								placeholder="영문과 특수문자를 포함한 최소 8자"></td>
						</tr>
						<tr>
							<td style="text-align: left">
								<p><strong>비밀번호를 재확인해주세요.</strong>&nbsp;&nbsp;&nbsp;<span id="pwChk2"></span></p>
							</td>
						</tr>
						<tr>
							<td><input type="password" size="17" maxlength="20" id="password_check"
								name="pw_check" class="form-control tooltipstered" 
								maxlength="20" required="required" aria-required="true"
								style="ime-mode: inactive; margin-bottom: 25px; height: 40px; border: 1px solid #d9d9de"
								placeholder="비밀번호가 일치해야합니다."></td>
						</tr>

						<tr>
							<td style="text-align: left">
								<p><strong>이름을 입력해주세요.</strong>&nbsp;&nbsp;&nbsp;<span id="nameChk"></span></p>
							</td>
						</tr>
						<tr>
							<td><input type="text" name="userName" id="user_name"
								class="form-control tooltipstered" maxlength="6"
								required="required" aria-required="true"
								style="margin-bottom: 25px; width: 100%; height: 40px; border: 1px solid #d9d9de"
								placeholder="한글로 최대 6자"></td>
						</tr>

						<tr>
							<td style="padding-top: 10px; text-align: center">
								<p><strong>회원가입을 환영합니다~~!</strong></p>
							</td>
						</tr>
						<tr>
							<td style="width: 100%; text-align: center; colspan: 2;"><input
								type="button" value="회원가입" 
								class="btn form-control tooltipstered" id="signup-btn"
								style="background-color: #643691; margin-top: 0; height: 40px; color: white; border: 0px solid #388E3C; opacity: 0.8">
							</td>
						</tr>

					</table>
			
			</div>
		</div>
	</div>

 

 


2. 검증로직 작성(아이디 공백란)

 

$(function() {
      
      //각 입력값들의 유효성 검증을 위한 정규표현식을 변수로 선언.
         const getIdCheck = RegExp(/^[a-zA-Z0-9]{4,14}$/);
         const getPwCheck = RegExp(/([a-zA-Z0-9].*[!,@,#,$,%,^,&,*,?,_,~])|([!,@,#,$,%,^,&,*,?,_,~].*[a-zA-Z0-9])/);
         const getNameCheck = RegExp(/^[가-힣]+$/);
         
      // 입력값 중 하나라도 만족하지 못한다면 회원가입 처리를 막기위한 논리형 변수 선언.
     	 let chk1 = false, chk2 = false, chk3 = false, chk4 = false;
     	    
      //회원가입시 사용자의 입력값 검증!
      
      //1. ID입력값 검증 
      $('#user_id').keyup(function() {
         if($(this).val() === '' ) {//지금 발생하는 이곳이 빈 문자열이니?
            $(this).css('background-color', 'pink');
            $('#idChk').html('<b style="font-size: 14px; color: red">[아이디는 필수값입니다.]</b>');//텍스트를 집어넣을거야 
            chk1 = false;
         }

 

나중에 입력값 유효성 검사 진행  let chk1 = false, chk2 = false, chk3 = false, chk4 = false;
if 각 부분에 chk1 = false 등 
if로 && 앤드기호로 다 트루값이 왔는지 확인 후 이벤트 종료

 


3. DB와 연동하여 비동기 통신으로 아이디 중복값 확인

 

 

//아이디 입력값 유효성 검사(영문으로만 4~14글자 허용)
         //정규표현식변수.test('검증하고 싶은 값')  => return boolean type
         //정규표현식에 어긋난 값이면 false, 올바른 값이면true를 반환
         else if(!getIdCheck.test($(this).val())) {//정규표현식이 틀렸다면
            $(this).css('background-color', 'pink');
            $('#idChk').html('<b style="font-size: 14px; color: red">[영문자, 숫자조합 4-14로쓰세요]</b>');
            chk1 = false;
         }else{//아이디 중복확인 ajax 사용, 특정 호직이 실행이 끝날 때까지 기다리지 않고 먼저 코드를 샐행 (페이지 전환 없이 통신)
        	 
         	//ID중복 확인 통신을 위해 입력값을 가져오기
         	const id = $(this).val();
         
         	//ajax 호출.
         	//클라이언트에서 서버와 비동기 통신을 진행하는 ajax함수.
         	$.ajax({
         		 type :'post', // 서버에 전송하는 http방식
         		 url :'/user/checkId', // 서버 요청 url
         		 headers : {
         			 'Content-Type' : 'application/json'
         		 },
         		 dataType : 'text', //서버로 부터 응답받을 데이터의 형태 
        		 data : id, // 서버로 전송할 데이터 // 위에서 지정한 const id 
        		 success : function(result) { // 매개변수에 통신성공시 데이터가 저장된다.
					//서버와 통신성공시 실행할 내용 작성.
					console.log('통신 성공!' + result);
        		 	if(result === 'available'){
        		 		 $('#user_id').css('background-color', 'aqua');
        		 		 $('#idChk').html('<b style="font-size: 14px; color: green">[아이디 사용이 가능하다.]</b>');
        		 		 chk1 = true;
        		 	}else{
        		 		 $('#user_id').css('background-color', 'pink');
        		 		 $('#idChk').html('<b style="font-size: 14px; color: red">[아이디 중복!.]</b>');
        		 		 chk1 = false;
        		 	}
				},
				error : function (status, error) { //통신에 실패했을때
					console.log('통신실패');
					console.log(status, error)
				}
          	}); // end ajax(아이디 중복 확인)
         }

 

ajax의 사용법!

1.type은 get이나 post 같은 http method를 나타낸다.

2.url는 데이터를 받아올 페이지다. 이 페이지의 코드는 곧 설명하겠다.

3.data는 요청시에 함께 보낼 파라미터들이다. 난 그냥 안보낼거라 비워뒀다.

4.dataType은 받아올 데이터의 형식인데, 빼놔도 된다.

5.success는 성공시에 수행할 핸들러를 받는다.

6.error는 실패시에 수행할 핸들러를 받는다.

 


4. 컨트롤러 작성

@PostMapping("/checkId")
	public String checkId(@RequestBody String account) {	// 받을 데이터타입이 텍스트라 스트링으로함 반드시 리퀘스트바디를 붙힐것! ajax 통신시
		System.out.println("/user/checkId : post");
		System.out.println("param : " + account );
		
		int checkNum = serivce.checkId(account);
		
		if(checkNum == 1) {
			System.out.println("아이디가 중복되었다.");
			return "duplicated";
		}else {
			System.out.println("아이디 사용 가능");
			return "available";
		}
	}

@RequestBody String account의 값으로 ajax로 전달한 아이디가 account값으로 전달된다.

이때 컨트롤러에서는 UserService 객체로 전달하여 sql에 접근한다. 

이때 sql문을 count(*)을 썻으므로 1혹은 0이 값으로 오기 때문에 int로 값을 잡는다.

 


5. 서비스 작성

 

@Service
public class BoardService implements IBoardService {
	
	@Autowired
	private IBoardMapper mapper;

	@Override
	public void insert(BoardVO article) {
		mapper.insert(article);
	}

서비스에서 mapper로 값을 전달한다. 

 

즉 컨트롤러 > 서비스 > 인터페이스매퍼 > 매퍼.xml로 전달되어 SQL 쿼리문을 작동하게 된다. 

 

<select id="checkId" resultType="int">
     	select count(*) from mvc_user where account = #{account}
     	</select>

 

UserMapper.xml에서 이부분이 작동되게 된다. 

 


6. 최종 동작 확인 

 

컨트롤러에서 아이디가 중복 되었으면 duplicated 

아이디가 사용이 가능하면 available이 리턴되어 jsp파일로 가게 설정이 되어있다. 

다시 컨트롤러와 jsp를 보자

@PostMapping("/checkId")
	public String checkId(@RequestBody String account) {	// 받을 데이터타입이 텍스트라 스트링으로함 반드시 리퀘스트바디를 붙힐것! ajax 통신시
		System.out.println("/user/checkId : post");
		System.out.println("param : " + account );
		
		int checkNum = serivce.checkId(account);
		
		if(checkNum == 1) {
			System.out.println("아이디가 중복되었다.");
			return "duplicated";
		}else {
			System.out.println("아이디 사용 가능");
			return "available";
		}
	}
$.ajax({
         		 type :'post', // 서버에 전송하는 http방식
         		 url :'/user/checkId', // 서버 요청 url
         		 headers : {
         			 'Content-Type' : 'application/json'
         		 },
         		 dataType : 'text', //서버로 부터 응답받을 데이터의 형태 
        		 data : id, // 서버로 전송할 데이터 // 위에서 지정한 const id 
        		 success : function(result) { // 매개변수에 통신성공시 데이터가 저장된다.
					//서버와 통신성공시 실행할 내용 작성.
					console.log('통신 성공!' + result);
        		 	if(result === 'available'){
        		 		 $('#user_id').css('background-color', 'aqua');
        		 		 $('#idChk').html('<b style="font-size: 14px; color: green">[아이디 사용이 가능하다.]</b>');
        		 		 chk1 = true;
        		 	}else{
        		 		 $('#user_id').css('background-color', 'pink');
        		 		 $('#idChk').html('<b style="font-size: 14px; color: red">[아이디 중복!.]</b>');
        		 		 chk1 = false;
        		 	}
				},
				error : function (status, error) { //통신에 실패했을때
					console.log('통신실패');
					console.log(status, error)
				}
          	}); // end ajax(아이디 중복 확인)

 


7. 동작확인 

 

필수 값을 누락 하였을때
아이디 입력란을 공란으로 둘경우
모든 조건을 충족할 경우
아이디가 중복일 경우

반응형
반응형

학원에서 강의 겸 정리하는 회원가입 진행순서 테스트 모듈까지 

 

1. src -> resouces > web-inf - spring > mvc_config에서 

 

마이바티스 스프링 스캔 및 빈 등록을 해준다. 

 

2. 빈 등록을 마쳤으면

 

user > model > class를 만든다. userVO라는 모델클래스를 만들어 

안에 내용은 private를 사용하여 getter , setter tostring까지 생성해준다. 

 

3.

repositroy에 iUserMapper라는 인터페이스를 생성해준다. 

해당 내용은 

 

public interface IUserMapper {

//아이디 중복체크 기능
int checkId(String account);

//회원 가입기능
void register(UserVO vo);

//회원 정보 조회 기능 
UserVO selectOne(String account);

//회원 탈퇴 기능 
void delete(String account);
}

 

이정도의 추상 메소드를 작성해준다 .

 

 

4. Mapper.xml 제작

1.에서 만들었던

이형식에 맞추어 파일을 생성한다. 

 

마이바티스 xml이라는 걸 알기 위해 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> 를 상단에 붙혀 준다. 

 

mapper라는 태그를 만들어 준다 이때 namespace에 IuserMapper의 주소값을 넣어준다

필자는 . <mapper namespace="com.spring.mvc.user.repository.IUserMapper"> 이런식으로 넣었다. 

 

5. Test.class파일 제작 

 

src/test/java > 경로 > user 폴더 

 

만들어 준뒤 어노테이션으로 runwith와 contextconfiguration을 입력 주소값은 자동완성으로 완성된다

 

테스트값을 위해 아까 만들어주었던 iuserMapper 객체를 사용한다. 

 

회원가입, 중복값 체크 해당 아이디 찾기 ,아이디 삭제 기능을 테스트 값으로 입력하였댜.

테스트에 메소드는 이렇게 4개를 만들어 주고 이제 아까 만들었던 userMapper.xml에서 sql문만 완성시켜주면 

테스트는 완성이된다. 

 

6.xml파일에 sql문 입력

 

resultmap에는 uservo의 변수명과 오라클의 컬럼값이 다를때 사용한다. 

prioperty에는 uservo의 변수명을 입력하고 , column값에는 테이블의 컬럼값을 입력한다. 

 

각 태그의 id값은 IuserMapper의 메소드명과 똑같이 작성한다. 

이때 <select> 문에서 resulttype값을 int로 가져온다. 왜냐하면 count(*)함수를 입력했을때 1또는 0이 나오기 떄문이다. 

그리고 selectone에서 resultMap의 경우는 regdate의 값이 달라 resultmap을 참고한다. 

 

이렇게 sql문이 완성이 되었으면 이제 테스트를 시작해보자.

 

7. 테스트 실행

 

outline에서 해당 메소드를 클릭한뒤 runas > junit을 실행하면된다. 

 

성공시 화면

값이 정상적으로 들어왔다. 

 

실패시 화면

 

실패시 로딩바가 빨간색으로 변하며 아래 모니터 모양에 오류코드가 나오게 된다. 

 

ora-00001은 중복값이 발생하여서 그렇다. 

 

다음 포스팅에는 화면구현까지 알아보자.

반응형

+ Recent posts