관리 메뉴

거니의 velog

231205_SPRING 2 (10-1) 본문

대덕인재개발원/대덕인재개발원_웹기반 애플리케이션

231205_SPRING 2 (10-1)

Unlimited00 2023. 12. 5. 08:32
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<section class="content-header">
    <div class="container-fluid">
        <div class="row mb-2">
            <div class="col-sm-6">
                <h1>마이페이지</h1>
            </div>
            <div class="col-sm-6">
                <ol class="breadcrumb float-sm-right">
                    <li class="breadcrumb-item"><a href="#">Home</a></li>
                    <li class="breadcrumb-item active">User Profile</li>
                </ol>
            </div>
        </div>
    </div>
</section>

<section class="content">
    <div class="container-fluid">
        <div class="row">
            <div class="col-md-3">

                <!-- Profile Image -->
                <div class="card card-dark card-outline">
                    <div class="card-header">
                        <div class="card-title">
                            <h4>내정보</h4>
                        </div>
                    </div>
                    <div class="card-body">
                        <div class="position-relative">
                            <img src="${pageContext.request.contextPath }/resources/dist/img/photo1.png" alt="Photo 1" class="img-fluid">
                            <div class="ribbon-wrapper ribbon-lg">
                                <div class="ribbon bg-success text-lg">
                                    Profile
                                </div>
                            </div>
                        </div>
                        <div class="row mt-4">
                            <div class="col-md-4 text-bold">아이디</div>
                            <div class="col-md-8">a001</div>
                        </div>
                        <div class="row mt-2">
                            <div class="col-md-4 text-bold">비밀번호</div>
                            <div class="col-md-8">PROTECTED</div>
                        </div>
                        <div class="row mt-2">
                            <div class="col-md-4 text-bold">이름</div>
                            <div class="col-md-8">홍길동</div>
                        </div>
                        <div class="row mt-2">
                            <div class="col-md-4 text-bold">성별</div>
                            <div class="col-md-8">남자</div>
                        </div>
                        <div class="row mt-2">
                            <div class="col-md-4 text-bold">이메일</div>
                            <div class="col-md-8">ddit@ddit.or.kr</div>
                        </div>
                        <div class="row mt-2">
                            <div class="col-md-4 text-bold">전화번호</div>
                            <div class="col-md-8">010-1234-1234</div>
                        </div>
                        <div class="row mt-2">
                            <div class="col-md-4 text-bold">주소</div>
                            <div class="col-md-8">대전광역시 중구 오류동 471-23 대덕인재개발원</div>
                        </div>
                    </div>
                </div>
            </div>
            <div class="col-md-9">
                <div class="card card-dark card-outline">
                    <div class="card-header">
                        <div class="row">
                            <div class="col-md-10">
                                <h4>내정보 수정</h4>
                            </div>
                            <div class="col-md-2" align="right">
                                <button type="submit" class="btn btn-info">수정하기</button>
                            </div>
                        </div>
                    </div>
                    <div class="card-body">
                        <div class="tab-content">
                            <div class="tab-pane active">
                                <form class="form-horizontal">
                                    <div class="form-group row">
                                        <label class="col-sm-2 col-form-label">프로필이미지</label>
                                        <div class="col-md-10">
                                            <div class="custom-file">
                                                <input type="file" class="custom-file-input" id="imgFile" name="imgFile">
                                                <label class="custom-file-label" for="imgFile">프로필 이미지를 선택해주세요</label>
                                            </div>
                                        </div>
                                    </div>
                                    <div class="form-group row">
                                        <label for="memId" class="col-sm-2 col-form-label">아이디</label>
                                        <div class="col-sm-10">
                                            <input type="text" class="form-control" id="memId" name="memId" placeholder="아이디를 입력해주세요.">
                                        </div>
                                    </div>
                                    <div class="form-group row">
                                        <label for="memPw" class="col-sm-2 col-form-label">비밀번호</label>
                                        <div class="col-sm-10">
                                            <input type="password" class="form-control" id="memPw" name="memPw" placeholder="비밀번호를 입력해주세요.">
                                        </div>
                                    </div>
                                    <div class="form-group row">
                                        <label for="memName" class="col-sm-2 col-form-label">이름</label>
                                        <div class="col-sm-10">
                                            <input type="text" class="form-control" id="memName" name="memName" placeholder="이름을 입력해주세요.">
                                        </div>
                                    </div>
                                    <div class="form-group row">
                                        <label for="" class="col-sm-2 col-form-label">성별</label>
                                        <div class="col-sm-10">
                                            <div class="icheck-primary d-inline">
                                                <input type="radio" id="memGenderM" name="memGender" value="M" checked="">
                                                <label for="memGenderM">남자</label>
                                            </div>
                                            <div class="icheck-primary d-inline">
                                                <input type="radio" id="memGenderF" name="memGender" value="F">
                                                <label for="memGenderF">여자</label>
                                            </div>
                                        </div>
                                    </div>
                                    <div class="form-group row">
                                        <label for="memEmail" class="col-sm-2 col-form-label">이메일</label>
                                        <div class="col-sm-10">
                                            <input type="text" class="form-control" id="memEmail" name="memEmail" placeholder="이메일을 입력해주세요.">
                                        </div>
                                    </div>
                                    <div class="form-group row">
                                        <label for="memPhone" class="col-sm-2 col-form-label">전화번호</label>
                                        <div class="col-sm-10">
                                            <input type="text" class="form-control" id="memPhone" name="memPhone" placeholder="전화번호를 입력해주세요.">
                                        </div>
                                    </div>
                                    <div class="input-group mb-3">
                                        <label for="inputSkills" class="col-sm-2 col-form-label">주소</label>
                                        <div class="col-sm-10">
                                            <div class="input-group mb-3">
                                                <input type="text" class="form-control" placeholder="우편번호를 입력해주세요">
                                                <span class="input-group-append">
                                                    <button type="button" onclick="sample6_execDaumPostcode()" class="btn btn-secondary btn-flat">우편번호 찾기</button>
                                                </span>
                                            </div>
                                            <div class="input-group mb-3">
                                                <input type="text" class="form-control" placeholder="주소를 입력해주세요">
                                            </div>
                                            <div class="input-group mb-3">
                                                <input type="text" class="form-control" placeholder="상세주소를 입력해주세요">
                                            </div>
                                        </div>
                                    </div>
                                </form>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</section>
package kr.or.ddit.controller.crud.notice;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Controller
@RequestMapping("/notice")
public class NoticeProfileController {

	@RequestMapping(value = "/profile.do", method = RequestMethod.GET)
	public String noticeProfile() {
		return "notice/profile";
	}
	
}

- http://localhost/notice/profile.do


-- 회원 테이블 작성
create table noticemember(                                             
	mem_no number(8) not null,                                       
	mem_id varchar2(100) not null,                                
	mem_pw varchar2(100) not null,                             
	mem_name varchar2(100) not null,                               
	mem_gender varchar2(30) not null,                                          
	mem_email varchar2(150) not null,
    mem_phone varchar2(150) not null,
    mem_postcode varchar2(30) not null,
    mem_address1 varchar2(300) not null,
    mem_address2 varchar2(300) not null,
    mem_agree varchar2(30) not null,
    mem_profileimg varchar2(500) not null,
    mem_regdate date not null,
    enabled varchar2(2) not null,
	constraint pk_noticemember primary key(mem_no)                      
); 

create sequence seq_noticemember increment by 1 start with 1 nocache;  

create table noticemember_auth(
	mem_no number(8) not null,
	auth varchar2(50) not null,
	constraint fk_noticemember_auth_mem_no foreign key(mem_no) references noticemember(mem_no)
);

commit;

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<div class="login-box">
    <div class="card">
        <div class="card-body login-card-body">
            <h2 class="login-box-msg"><b>DDIT</b> BOARD</h2>

            <form action="/notice/loginCheck.do" method="post" id="signForm" name="signForm">
                <div class="input-group mb-3">
                    <input type="text" id="memId" name="memId" class="form-control" placeholder="아이디를 입력해주세요">
                    <div class="input-group-append">
                        <div class="input-group-text">
                            <span class="fas fa-envelope"></span>
                        </div>
                    </div>
                    <span class="error invalid-feedback" style="display: block;">${errors.memId }</span>
                </div>
                <div class="input-group mb-3">
                    <input type="password" id="memPw" name="memPw" class="form-control" placeholder="비밀번호를 입력해주세요">
                    <div class="input-group-append">
                        <div class="input-group-text">
                            <span class="fas fa-lock"></span>
                        </div>
                    </div>
                    <span class="error invalid-feedback" style="display: block;">${errors.memPw }</span>
                </div>
                <div class="row">
                    <div class="col-8">
                        <div class="icheck-primary">
                            <input type="checkbox" id="remember">
                            <label for="remember">
                                Remember Me
                            </label>
                        </div>
                    </div>
                    <div class="col-4">
                        <button type="button" class="btn btn-dark btn-block" id="loginBtn">로그인</button>
                    </div>
                </div>
            </form>

            <p class="mb-1">
                <a href="">아이디&비밀번호 찾기</a>
            </p>
            <p class="mb-0">
                <a href="" class="text-center">회원가입</a>
            </p>
        </div>
    </div>
</div>
<script>
	$(function(){
		// 로그인 페이지에서 사용할 배경 이미지 설정
		$("body").css("background-image", "url('${pageContext.request.contextPath }/resources/dist/img/background04.jpg')").css("background-size", "cover");
		
		var signForm = $("#signForm");
		var loginBtn = $("#loginBtn");
		
		loginBtn.on("click", function(){
			var id = $("#memId").val();
			var pw = $("#memPw").val();
			
			/* if(!id) {
				alert("아이디를 입력해 주세요.");
				return false;
			}
			
			if(!pw) {
				alert("비밀번호를 입력해 주세요.");
				return false;
			} */
			
			signForm.submit();
		});
	});
</script>
package kr.or.ddit.vo.crud;

import lombok.Data;

@Data
public class NoticeMemberAuthVO {

	private int memNo;
	private String auth;
	
}
package kr.or.ddit.vo.crud;

import java.util.List;

import org.springframework.web.multipart.MultipartFile;

import lombok.Data;

@Data
public class NoticeMemberVO {

	private int memNo;
	private String memId;
	private String memPw;
	private String memName;
	private String memGender;
	private String memEmail;
	private String memPhone;
	private String memPostCode;
	private String memAddress1;
	private String memAddress2;
	private String memAgree;
	private String memRegDate;
	private String enabled;
	
	private MultipartFile imgFile;
	private String memProfileImg;
	
	private List<NoticeMemberAuthVO> authList;
	
}
package kr.or.ddit.controller.crud.notice;

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

import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import kr.or.ddit.service.INoticeService;
import kr.or.ddit.vo.crud.NoticeMemberVO;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Controller
@RequestMapping("/notice")
public class NoticeLoginController {

	@Inject
	private INoticeService noticeService;
	
	@RequestMapping(value = "/login.do", method = RequestMethod.GET)
	public String noticeLogin(Model model) {
		model.addAttribute("bodyText", "login-page");
		return "conn/login";
	}
	
	@RequestMapping(value = "/loginCheck.do", method = RequestMethod.POST)
	public String loginCheck(
			HttpServletRequest req, 
			NoticeMemberVO member, 
			Model model,
			RedirectAttributes ra
			) {
		String goPage = "";
		
		Map<String, String> errors = new HashMap<String, String>();
		
		if(StringUtils.isBlank(member.getMemId())) {
			errors.put("memId", "아이디를 입력해 주세요.");
		}
		if(StringUtils.isBlank(member.getMemPw())) {
			errors.put("memPw", "비밀번호를 입력해 주세요.");
		}
		
		if(errors.size() > 0) { // 에러 정보가 존재
			model.addAttribute("errors", errors);
			model.addAttribute("member", member);
			model.addAttribute("bodyText", "login-page");
			goPage = "conn/login";
		}else { // 정상적인 데이터
			NoticeMemberVO memberVO = noticeService.loginCheck(member);
			if(memberVO != null) { // 로그인 성공
				HttpSession session = req.getSession();
				session.setAttribute("SessionInfo", memberVO);
				ra.addFlashAttribute("message", "로그인 성공!");
				goPage = "redirect:/notice/list.do";
			}else { // 로그인 실패
				model.addAttribute("message", "서버에러, 로그인 정보를 정확하게 확인해 주세요.");
				model.addAttribute("member", member);
				model.addAttribute("bodyText", "login-page");
				goPage = "conn/login";
			}
		}
		
		return goPage;
	}
	
	@RequestMapping(value = "/signup.do", method = RequestMethod.GET)
	public String signupForm(Model model) {
		model.addAttribute("bodyText", "register-page");
		return "conn/register";
	}
	
}
package kr.or.ddit.service;

import java.util.List;

import kr.or.ddit.ServiceResult;
import kr.or.ddit.vo.crud.NoticeMemberVO;
import kr.or.ddit.vo.crud.NoticeVO;
import kr.or.ddit.vo.crud.PaginationInfoVO;

public interface INoticeService {

	public int selectNoticeCount(PaginationInfoVO<NoticeVO> pagingVO);
	public List<NoticeVO> selectNoticeList(PaginationInfoVO<NoticeVO> pagingVO);
	public ServiceResult insertNotice(NoticeVO noticeVO);
	public NoticeVO selectNotice(int boNo);
	public ServiceResult updateNotice(NoticeVO noticeVO);
	public ServiceResult deleteNotice(int boNo);
    
	public NoticeMemberVO loginCheck(NoticeMemberVO member);

}
package kr.or.ddit.service.impl;

import java.util.List;

import javax.inject.Inject;

import org.springframework.stereotype.Service;

import kr.or.ddit.ServiceResult;
import kr.or.ddit.mapper.ILoginMapper;
import kr.or.ddit.mapper.INoticeMapper;
import kr.or.ddit.service.INoticeService;
import kr.or.ddit.vo.crud.NoticeMemberVO;
import kr.or.ddit.vo.crud.NoticeVO;
import kr.or.ddit.vo.crud.PaginationInfoVO;

@Service
public class NoticeServiceImpl implements INoticeService {

	@Inject
	private INoticeMapper noticeMapper;
	@Inject
	private ILoginMapper loginMapper;
	
	@Override
	public int selectNoticeCount(PaginationInfoVO<NoticeVO> pagingVO) {
		return noticeMapper.selectNoticeCount(pagingVO);
	}

	@Override
	public List<NoticeVO> selectNoticeList(PaginationInfoVO<NoticeVO> pagingVO) {
		return noticeMapper.selectNoticeList(pagingVO);
	}

	@Override
	public ServiceResult insertNotice(NoticeVO noticeVO) {
		ServiceResult result = null;
		
		int status = noticeMapper.insertNotice(noticeVO);
		if(status > 0) {
			result = ServiceResult.OK;
		}else {
			result = ServiceResult.FAILED;
		}
		
		return result;
	}

	@Override
	public NoticeVO selectNotice(int boNo) {
		noticeMapper.incrementHit(boNo); // 게시글 조회수 증가
		return noticeMapper.selectNotice(boNo); // 게시글 번호에 해당하는 게시글 정보 가져오기
	}

	@Override
	public ServiceResult updateNotice(NoticeVO noticeVO) {
		ServiceResult result = null;
		
		int status = noticeMapper.updateNotice(noticeVO);
		if(status > 0) {
			result = ServiceResult.OK;
		}else {
			result = ServiceResult.FAILED;
		}
		
		return result;
	}

	@Override
	public ServiceResult deleteNotice(int boNo) {
		ServiceResult result = null;
		
		int status = noticeMapper.deleteNotice(boNo);
		if(status > 0) {
			result = ServiceResult.OK;
		}else {
			result = ServiceResult.FAILED;
		}
		
		return result;
	}

	@Override
	public NoticeMemberVO loginCheck(NoticeMemberVO member) {
		return loginMapper.loginCheck(member);
	}

}
package kr.or.ddit.mapper;

import kr.or.ddit.vo.crud.NoticeMemberVO;

public interface ILoginMapper {

	public NoticeMemberVO loginCheck(NoticeMemberVO member);

}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="kr.or.ddit.mapper.ILoginMapper">
	
	<!-- ctrl + shift + y : 소문자 변환 -->
	<select id="loginCheck" parameterType="noticeMemberVO" resultType="noticeMemberVO">
		select 
			mem_no,
			mem_id,
			mem_pw,
			mem_name,
			mem_gender,
			mem_email,
			mem_phone,
			mem_postcode,
			mem_address1,
			mem_address2,
			mem_agree,
			mem_profileimg,
			mem_regdate
		from noticemember 
		where mem_id = #{memId} 
		and mem_pw = #{memPw}
	</select>
	
</mapper>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
	
	<!--  
		[마이바티스] 스프링에서 "_"를 사용한 컬럼명 사용 시(BOOK 테이블의 BOOK_ID와 같은 컬럼)
		카멜케이스로 읽어주는 역할(bookId와 같이)
		
		ex) 테이블 컬럼명이 member_id인 경우 jsp 화면 단에서 이 값을 사용시 memberId로 사용할 수 있다.
	-->
	<settings>
		<setting name="mapUnderscoreToCamelCase" value="true"/>
	</settings>
	
	<typeAliases>
		<typeAlias type="kr.or.ddit.vo.Board" alias="board"/>
		<typeAlias type="kr.or.ddit.vo.crud.CrudMember" alias="crudMember"/>
		<typeAlias type="kr.or.ddit.vo.crud.CrudMemberAuth" alias="crudMemberAuth"/>
		
		<typeAlias type="kr.or.ddit.vo.test.TagBoardVO" alias="tagboardVO"/>
		<typeAlias type="kr.or.ddit.vo.test.TagVO" alias="tagVO"/>
		
		<typeAlias type="kr.or.ddit.vo.Item" alias="item"/>
		<typeAlias type="kr.or.ddit.vo.Item2" alias="item2"/>
		<typeAlias type="kr.or.ddit.vo.Item3" alias="item3"/>
		
		<typeAlias type="kr.or.ddit.vo.crud.PaginationInfoVO" alias="pagingVO"/>
		
		<typeAlias type="kr.or.ddit.vo.crud.NoticeVO" alias="noticeVO"/>
		<typeAlias type="kr.or.ddit.vo.crud.NoticeFileVO" alias="noticefileVO"/>
		
		<typeAlias type="kr.or.ddit.vo.crud.NoticeMemberVO" alias="noticeMemberVO"/>
		<typeAlias type="kr.or.ddit.vo.crud.NoticeMemberAuthVO" alias="noticeMemberAuthVO"/>
	</typeAliases>
	
</configuration>

- http://localhost/notice/login.do

[loginTemplate.jsp]

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://tiles.apache.org/tags-tiles" prefix="tiles" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>AdminLTE 3 | Log in</title>

    <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,400i,700&display=fallback">
    <link rel="stylesheet" href="${pageContext.request.contextPath }/resources/plugins/fontawesome-free/css/all.min.css">
    <link rel="stylesheet" href="${pageContext.request.contextPath }/resources/plugins/icheck-bootstrap/icheck-bootstrap.min.css">
    <link rel="stylesheet" href="${pageContext.request.contextPath }/resources/dist/css/adminlte.min.css">
    <script src="${pageContext.request.contextPath }/resources/plugins/jquery/jquery.min.js"></script>
</head>

<c:if test="${not empty message }">
	<script type="text/javascript">
		alert("${message}");
		<c:remove var="message" scope="request"/>
		<c:remove var="message" scope="session"/>
	</script>
</c:if>

<body class="hold-transition ${bodyText }">
    <!-- content 영역 -->
    <tiles:insertAttribute name="content" />

    <script src="${pageContext.request.contextPath }/resources/plugins/bootstrap/js/bootstrap.bundle.min.js"></script>
    <script src="${pageContext.request.contextPath }/resources/dist/js/adminlte.min.js"></script>
</body>

</html>

 

            <p class="mb-1">
                <a href="">아이디&비밀번호 찾기</a>
            </p>
            <p class="mb-0">
                <a href="/notice/signup.do" class="text-center">회원가입</a>
            </p>

- http://localhost/notice/signup.do

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<div class="register-box">
    <div class="card card-outline card-danger mt-4 mb-4">
        <div class="card-header text-center">
            <a href="" class="h1"><b>DDIT</b>BOARD</a>
        </div>
        <div class="card-body">
            <p class="login-box-msg">회원가입</p>

            <form action="/notice/signup.do" method="post" id="signupForm" enctype="multipart/form-data">
                <div class="input-group mb-3 text-center">
                    <img class="profile-user-img img-fluid img-circle" id="profileImg" src="${pageContext.request.contextPath }/resources/dist/img/AdminLTELogo.png" alt="User profile picture" style="width: 150px;">
                </div>
                <div class="input-group mb-3">
                    <label for="inputDescription">프로필 이미지</label>
                </div>
                <div class="input-group mb-3">
                    <div class="custom-file">
                        <input type="file" class="custom-file-input" name="imgFile" id="imgFile">
                        <label class="custom-file-label" for="imgFile">프로필 이미지를 선택해주세요</label>
                    </div>
                </div>
                <div class="input-group mb-3">
                    <label for="inputDescription">프로필 정보</label>
                </div>
                <div class="input-group mb-3">
                    <input type="text" class="form-control" id="memId" name="memId" placeholder="아이디를 입력해주세요">
                    <span class="input-group-append">
                        <button type="button" class="btn btn-secondary btn-flat" id="idCheckBtn">중복확인</button>
                    </span>
                    <span class="error invalid-feedback" style="display:block;"></span>
                </div>
                <div class="input-group mb-3">
                    <input type="text" class="form-control" id="memPw" name="memPw" placeholder="비밀번호를 입력해주세요">
                    <span class="error invalid-feedback" style="display:block;"></span>
                </div>
                <div class="input-group mb-3">
                    <input type="text" class="form-control" id="memName" name="memName" placeholder="이름을 입력해주세요">
                    <span class="error invalid-feedback" style="display:block;"></span>
                </div>
                <div class="input-group mb-3">
                    <div class="form-group clearfix">
                        <div class="icheck-primary d-inline">
                            <input type="radio" id="memGenderM" name="memGender" value="M" checked="checked">
                            <label for="memGenderM">남자&nbsp;</label>
                        </div>
                        <div class="icheck-primary d-inline">
                            <input type="radio" id="memGenderF" name="memGender" value="F">
                            <label for="memGenderF">여자 </label>
                        </div>
                    </div>
                </div>
                <div class="input-group mb-3">
                    <input type="text" class="form-control" id="memEmail" name="memEmail" placeholder="이메일을 입력해주세요">
                </div>
                <div class="input-group mb-3">
                    <input type="text" class="form-control" id="memPhone" name="memPhone" placeholder="전화번호를 입력해주세요">
                </div>
                <div class="input-group mb-3">
                    <input type="text" class="form-control" id="memPostCode" name="memPostCode">
                    <span class="input-group-append">
                        <button type="button" class="btn btn-secondary btn-flat">우편번호 찾기</button>
                    </span>
                </div>
                <div class="input-group mb-3">
                    <input type="text" class="form-control" id="memAddress1" name="memAddress1" placeholder="주소를 입력해주세요">
                </div>
                <div class="input-group mb-3">
                    <input type="text" class="form-control" id="memAddress2" name="memAddress2" placeholder="상세주소를 입력해주세요">
                </div>
                <div class="row">
                    <div class="col-8">
                        <div class="icheck-primary">
                            <input type="checkbox" id="memAgree" name="memAgree" value="Y">
                            <label for="memAgree">개인정보처리방침</label>
                        </div>
                    </div>
                    <div class="col-4">
                        <button type="button" class="btn btn-dark btn-block" id="signupBtn">가입하기</button>
                    </div>
                    <button type="button" class="btn btn-secondary btn-block mt-4" onclick="javascript:location.href='/notice/login.do'">뒤로가기</button>
                </div>
            </form>
        </div>
    </div>
</div>
<script>
	$(function(){
		// 로그인 페이지에서 사용할 배경 이미지 설정
		$("body").css("background-image", "url('${pageContext.request.contextPath }/resources/dist/img/background04.jpg')").css("background-size", "cover");
		
		var signupForm = $("#signupForm");		// 폼 체크 Element
		var signupBtn = $("#signupBtn");		// 가입하기 버튼 Element
		var idCheckBtn = $("#idCheckBtn");		// 중복확인 버튼 Element
		var idCheckFlag = false;				// 중복확인 flag
		
		idCheckBtn.on("click", function(){
			var id = $("#memId").val();
			
			if(!id) {
				alert("아이디를 입력해 주세요!");
				return false;
			}
			
			var data = {
				memId : id
			};
			
			$.ajax({
				type: "post",
				url: "/notice/idCheck.do",
				data: JSON.stringify(data),
				contentType: "application/json;charset=utf-8",
				success: function(res) {
					console.log("중복확인 후 넘겨받은 결과 : " + res);
					
					if(res == "NOTEXIST") { // 아이디 사용 가능
						alert("사용 가능한 아이디 입니다.");
						idCheckFlag = true; // 중복 확인 했다는 flag 설정
					}else { // 아이디 중복
						alert("이미 사용중인 아이디 입니다.");
						//idCheckFlag = false; // 중복 확인 했다는 flag 설정
					}
				}
			});
		});
		
		signupBtn.on("click", function(){
			
		});
	});
</script>
package kr.or.ddit.controller.crud.notice;

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

import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import kr.or.ddit.ServiceResult;
import kr.or.ddit.service.INoticeService;
import kr.or.ddit.vo.crud.NoticeMemberVO;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Controller
@RequestMapping("/notice")
public class NoticeLoginController {

	@Inject
	private INoticeService noticeService;
	
	@RequestMapping(value = "/login.do", method = RequestMethod.GET)
	public String noticeLogin(Model model) {
		model.addAttribute("bodyText", "login-page");
		return "conn/login";
	}
	
	@RequestMapping(value = "/loginCheck.do", method = RequestMethod.POST)
	public String loginCheck(
			HttpServletRequest req, 
			NoticeMemberVO member, 
			Model model,
			RedirectAttributes ra
			) {
		String goPage = "";
		
		Map<String, String> errors = new HashMap<String, String>();
		
		if(StringUtils.isBlank(member.getMemId())) {
			errors.put("memId", "아이디를 입력해 주세요.");
		}
		if(StringUtils.isBlank(member.getMemPw())) {
			errors.put("memPw", "비밀번호를 입력해 주세요.");
		}
		
		if(errors.size() > 0) { // 에러 정보가 존재
			model.addAttribute("errors", errors);
			model.addAttribute("member", member);
			model.addAttribute("bodyText", "login-page");
			goPage = "conn/login";
		}else { // 정상적인 데이터
			NoticeMemberVO memberVO = noticeService.loginCheck(member);
			if(memberVO != null) { // 로그인 성공
				HttpSession session = req.getSession();
				session.setAttribute("SessionInfo", memberVO);
				ra.addFlashAttribute("message", "로그인 성공!");
				goPage = "redirect:/notice/list.do";
			}else { // 로그인 실패
				model.addAttribute("message", "서버에러, 로그인 정보를 정확하게 확인해 주세요.");
				model.addAttribute("member", member);
				model.addAttribute("bodyText", "login-page");
				goPage = "conn/login";
			}
		}
		
		return goPage;
	}
	
	@ResponseBody
	@RequestMapping(value = "/idCheck.do", method = RequestMethod.POST)
	public ResponseEntity<ServiceResult> idCheck(@RequestBody Map<String, String> map) {
		/*
		 * 단일 데이터를 꺼낼 때
		 * 0) ajax 설정에서 ContentType 설정을 하지 않고, 데이터만 {memId : id} 설정해서 넘길 때
		 * 		- String memId로 꺼낼 수 있다.
		 * 		> 이 형태는 쿼리스트링에 구성된 memId를 꺼낼 때도 동일함
		 * 
		 * 1) ajax 설정에서 ContentType 설정을 하지 않고, 데이터만 JSON.stringify() 일 때
		 * 		- @RequestBody로 String memId를 꺼나면 '!@#!%!@#@$A...' 이런 데이터가 넘어옴
		 * 
		 * 2) ajax 설정에서 ContentType 설정을 하고, 데이터만 JSON.stringify() 일 때(데이터 JSON 객체로 넘어감)
		 * 		- @RequestBody로 String memId를 꺼내면, '{memId : a001}' 데이터가 넘어옴
		 * 
		 * 3) ajax 설정에서 ContentType 설정을 하고, 데이터만 JSON.stringify() 일 때(데이터 JSON 객체로 넘어감)
		 * 		- @RequestParam으로 String memId를 꺼내면, 400 에러가 발생한다.
		 * 
		 * 4) ajax 설정에서 ContentType 설정을 하고, 데이터만 JSON.stringify() 일 때(데이터 JSON 객체로 넘어감)
		 * 		- @RequestBody Map<String, String> map을 꺼내면 'a001' 데이터가 넘어옴
		 * 
		 * 우리는 단일 데이터를 받아낼 때에 고민하지 말고 컬렉션 Map 또는 단일 데이터를 받을 VO를 파라미터로 설정하여 값을 받는다.
		 */
		
		log.info("넘겨받은 아이디 : " + map.get("memId"));
		ServiceResult result = noticeService.idCheck(map.get("memId"));
		return new ResponseEntity<ServiceResult>(result, HttpStatus.OK);
		
	}
	
	@RequestMapping(value = "/signup.do", method = RequestMethod.GET)
	public String signupForm(Model model) {
		model.addAttribute("bodyText", "register-page");
		return "conn/register";
	}
	
}
package kr.or.ddit.service;

import java.util.List;

import kr.or.ddit.ServiceResult;
import kr.or.ddit.vo.crud.NoticeMemberVO;
import kr.or.ddit.vo.crud.NoticeVO;
import kr.or.ddit.vo.crud.PaginationInfoVO;

public interface INoticeService {

	public int selectNoticeCount(PaginationInfoVO<NoticeVO> pagingVO);
	public List<NoticeVO> selectNoticeList(PaginationInfoVO<NoticeVO> pagingVO);
	public ServiceResult insertNotice(NoticeVO noticeVO);
	public NoticeVO selectNotice(int boNo);
	public ServiceResult updateNotice(NoticeVO noticeVO);
	public ServiceResult deleteNotice(int boNo);
	
	public NoticeMemberVO loginCheck(NoticeMemberVO member);
	public ServiceResult idCheck(String memId);

}
package kr.or.ddit.service.impl;

import java.io.File;
import java.util.List;
import java.util.UUID;

import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;

import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import kr.or.ddit.ServiceResult;
import kr.or.ddit.mapper.ILoginMapper;
import kr.or.ddit.mapper.INoticeMapper;
import kr.or.ddit.service.INoticeService;
import kr.or.ddit.vo.crud.NoticeMemberVO;
import kr.or.ddit.vo.crud.NoticeVO;
import kr.or.ddit.vo.crud.PaginationInfoVO;

@Service
public class NoticeServiceImpl implements INoticeService {

	@Inject
	private INoticeMapper noticeMapper;
	@Inject
	private ILoginMapper loginMapper;

	@Override
	public int selectNoticeCount(PaginationInfoVO<NoticeVO> pagingVO) {
		return noticeMapper.selectNoticeCount(pagingVO);
	}

	@Override
	public List<NoticeVO> selectNoticeList(PaginationInfoVO<NoticeVO> pagingVO) {
		return noticeMapper.selectNoticeList(pagingVO);
	}

	@Override
	public ServiceResult insertNotice(NoticeVO noticeVO) {
		ServiceResult result = null;

		int status = noticeMapper.insertNotice(noticeVO);
		if (status > 0) {
			result = ServiceResult.OK;
		} else {
			result = ServiceResult.FAILED;
		}

		return result;
	}

	@Override
	public NoticeVO selectNotice(int boNo) {
		noticeMapper.incrementHit(boNo); // 게시글 조회수 증가
		return noticeMapper.selectNotice(boNo); // 게시글 번호에 해당하는 게시글 정보 가져오기
	}

	@Override
	public ServiceResult updateNotice(NoticeVO noticeVO) {
		ServiceResult result = null;

		int status = noticeMapper.updateNotice(noticeVO);
		if (status > 0) {
			result = ServiceResult.OK;
		} else {
			result = ServiceResult.FAILED;
		}

		return result;
	}

	@Override
	public ServiceResult deleteNotice(int boNo) {
		ServiceResult result = null;

		int status = noticeMapper.deleteNotice(boNo);
		if (status > 0) {
			result = ServiceResult.OK;
		} else {
			result = ServiceResult.FAILED;
		}

		return result;
	}

	@Override
	public NoticeMemberVO loginCheck(NoticeMemberVO member) {
		return loginMapper.loginCheck(member);
	}

	@Override
	public ServiceResult idCheck(String memId) {
		ServiceResult result = null;

		NoticeMemberVO member = loginMapper.idCheck(memId);
		if (member != null) {
			result = ServiceResult.EXIST;
		} else {
			result = ServiceResult.NOTEXIST;
		}

		return result;
	}

}
package kr.or.ddit.mapper;

import kr.or.ddit.vo.crud.NoticeMemberVO;

public interface ILoginMapper {

	public NoticeMemberVO loginCheck(NoticeMemberVO member);
	public NoticeMemberVO idCheck(String memId);

}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="kr.or.ddit.mapper.ILoginMapper">
	
	<!-- ctrl + shift + y : 소문자 변환 -->
	<select id="loginCheck" parameterType="noticeMemberVO" resultType="noticeMemberVO">
		select 
			mem_no,
			mem_id,
			mem_pw,
			mem_name,
			mem_gender,
			mem_email,
			mem_phone,
			mem_postcode,
			mem_address1,
			mem_address2,
			mem_agree,
			mem_profileimg,
			mem_regdate
		from noticemember 
		where mem_id = #{memId} 
		and mem_pw = #{memPw}
	</select>
	
	<select id="idCheck" parameterType="string" resultType="noticeMemberVO">
		select 
			mem_no,
			mem_id,
			mem_pw,
			mem_name,
			mem_gender,
			mem_email,
			mem_phone,
			mem_postcode,
			mem_address1,
			mem_address2,
			mem_agree,
			mem_profileimg,
			mem_regdate
		from noticemember 
		where mem_id = #{memId} 
	</select>
	
</mapper>

- http://localhost/notice/signup.do


<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<div class="register-box">
    <div class="card card-outline card-danger mt-4 mb-4">
        <div class="card-header text-center">
            <a href="" class="h1"><b>DDIT</b>BOARD</a>
        </div>
        <div class="card-body">
            <p class="login-box-msg">회원가입</p>

            <form action="/notice/signup.do" method="post" id="signupForm" enctype="multipart/form-data">
                <div class="input-group mb-3 text-center">
                    <img class="profile-user-img img-fluid img-circle" id="profileImg" src="${pageContext.request.contextPath }/resources/dist/img/AdminLTELogo.png" alt="User profile picture" style="width: 150px;">
                </div>
                <div class="input-group mb-3">
                    <label for="inputDescription">프로필 이미지</label>
                </div>
                <div class="input-group mb-3">
                    <div class="custom-file">
                        <input type="file" class="custom-file-input" name="imgFile" id="imgFile">
                        <label class="custom-file-label" for="imgFile">프로필 이미지를 선택해주세요</label>
                    </div>
                </div>
                <div class="input-group mb-3">
                    <label for="inputDescription">프로필 정보</label>
                </div>
                <div class="input-group mb-3">
                    <input type="text" class="form-control" id="memId" name="memId" placeholder="아이디를 입력해주세요">
                    <span class="input-group-append">
                        <button type="button" class="btn btn-secondary btn-flat" id="idCheckBtn">중복확인</button>
                    </span>
                    <span class="error invalid-feedback" style="display:block;"></span>
                </div>
                <div class="input-group mb-3">
                    <input type="text" class="form-control" id="memPw" name="memPw" placeholder="비밀번호를 입력해주세요">
                    <span class="error invalid-feedback" style="display:block;"></span>
                </div>
                <div class="input-group mb-3">
                    <input type="text" class="form-control" id="memName" name="memName" placeholder="이름을 입력해주세요">
                    <span class="error invalid-feedback" style="display:block;"></span>
                </div>
                <div class="input-group mb-3">
                    <div class="form-group clearfix">
                        <div class="icheck-primary d-inline">
                            <input type="radio" id="memGenderM" name="memGender" value="M" checked="checked">
                            <label for="memGenderM">남자&nbsp;</label>
                        </div>
                        <div class="icheck-primary d-inline">
                            <input type="radio" id="memGenderF" name="memGender" value="F">
                            <label for="memGenderF">여자 </label>
                        </div>
                    </div>
                </div>
                <div class="input-group mb-3">
                    <input type="text" class="form-control" id="memEmail" name="memEmail" placeholder="이메일을 입력해주세요">
                </div>
                <div class="input-group mb-3">
                    <input type="text" class="form-control" id="memPhone" name="memPhone" placeholder="전화번호를 입력해주세요">
                </div>
                <div class="input-group mb-3">
                    <input type="text" class="form-control" id="memPostCode" name="memPostCode">
                    <span class="input-group-append">
                        <button type="button" class="btn btn-secondary btn-flat">우편번호 찾기</button>
                    </span>
                </div>
                <div class="input-group mb-3">
                    <input type="text" class="form-control" id="memAddress1" name="memAddress1" placeholder="주소를 입력해주세요">
                </div>
                <div class="input-group mb-3">
                    <input type="text" class="form-control" id="memAddress2" name="memAddress2" placeholder="상세주소를 입력해주세요">
                </div>
                <div class="row">
                    <div class="col-8">
                        <div class="icheck-primary">
                            <input type="checkbox" id="memAgree" name="memAgree" value="Y">
                            <label for="memAgree">개인정보처리방침</label>
                        </div>
                    </div>
                    <div class="col-4">
                        <button type="button" class="btn btn-dark btn-block" id="signupBtn">가입하기</button>
                    </div>
                    <button type="button" class="btn btn-secondary btn-block mt-4" onclick="javascript:location.href='/notice/login.do'">뒤로가기</button>
                </div>
            </form>
        </div>
    </div>
</div>
<script>
	$(function(){
		// 로그인 페이지에서 사용할 배경 이미지 설정
		$("body").css("background-image", "url('${pageContext.request.contextPath }/resources/dist/img/background04.jpg')").css("background-size", "cover");
		
		var signupForm = $("#signupForm");		// 폼 체크 Element
		var signupBtn = $("#signupBtn");		// 가입하기 버튼 Element
		var idCheckBtn = $("#idCheckBtn");		// 중복확인 버튼 Element
		var idCheckFlag = false;				// 중복확인 flag
		
		var imgFile = $("#imgFile");			// 파일선택 Element
		
		idCheckBtn.on("click", function(){
			var id = $("#memId").val();
			
			if(!id) {
				alert("아이디를 입력해 주세요!");
				return false;
			}
			
			var data = {
				memId : id
			};
			
			$.ajax({
				type: "post",
				url: "/notice/idCheck.do",
				data: JSON.stringify(data),
				contentType: "application/json;charset=utf-8",
				success: function(res) {
					console.log("중복확인 후 넘겨받은 결과 : " + res);
					
					if(res == "NOTEXIST") { // 아이디 사용 가능
						alert("사용 가능한 아이디 입니다.");
						idCheckFlag = true; // 중복 확인 했다는 flag 설정
					}else { // 아이디 중복
						alert("이미 사용중인 아이디 입니다.");
						//idCheckFlag = false; // 중복 확인 했다는 flag 설정
					}
				}
			});
		});
		
		imgFile.on("change", function(event){
			var file = event.target.files[0];
			
			if(isImageFile(file)) {
				var reader = new FileReader();
				reader.onload = function(e){
					$("#profileImg").attr("src", e.target.result);
				}
				reader.readAsDataURL(file);
			}else { // 이미지 파일이 아닐 때
				alert("이미지 파일을 선택해주세요!");
			}
		});
		
		signupBtn.on("click", function(){
			
		});
	});
	
	// 이미지 파일인지 체크
	function isImageFile(file){
		var ext = file.name.split(".").pop().toLowerCase(); // 파일명에서 확장자를 가져옵니다.
		return ($.inArray(ext, ["jpg", "jpeg", "gif", "png"]) === -1) ? false : true;
	}
</script>

- http://localhost/notice/signup.do


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

Daum 우편번호 서비스

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

postcode.map.daum.net

[register.jsp]

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<div class="register-box">
    <div class="card card-outline card-danger mt-4 mb-4">
        <div class="card-header text-center">
            <a href="" class="h1"><b>DDIT</b>BOARD</a>
        </div>
        <div class="card-body">
            <p class="login-box-msg">회원가입</p>

            <form action="/notice/signup.do" method="post" id="signupForm" enctype="multipart/form-data">
                <div class="input-group mb-3 text-center">
                    <img class="profile-user-img img-fluid img-circle" id="profileImg" src="${pageContext.request.contextPath }/resources/dist/img/AdminLTELogo.png" alt="User profile picture" style="width: 150px;">
                </div>
                <div class="input-group mb-3">
                    <label for="inputDescription">프로필 이미지</label>
                </div>
                <div class="input-group mb-3">
                    <div class="custom-file">
                        <input type="file" class="custom-file-input" name="imgFile" id="imgFile">
                        <label class="custom-file-label" for="imgFile">프로필 이미지를 선택해주세요</label>
                    </div>
                </div>
                <div class="input-group mb-3">
                    <label for="inputDescription">프로필 정보</label>
                </div>
                <div class="input-group mb-3">
                    <input type="text" class="form-control" id="memId" name="memId" placeholder="아이디를 입력해주세요">
                    <span class="input-group-append">
                        <button type="button" class="btn btn-secondary btn-flat" id="idCheckBtn">중복확인</button>
                    </span>
                    <span class="error invalid-feedback" style="display:block;"></span>
                </div>
                <div class="input-group mb-3">
                    <input type="text" class="form-control" id="memPw" name="memPw" placeholder="비밀번호를 입력해주세요">
                    <span class="error invalid-feedback" style="display:block;"></span>
                </div>
                <div class="input-group mb-3">
                    <input type="text" class="form-control" id="memName" name="memName" placeholder="이름을 입력해주세요">
                    <span class="error invalid-feedback" style="display:block;"></span>
                </div>
                <div class="input-group mb-3">
                    <div class="form-group clearfix">
                        <div class="icheck-primary d-inline">
                            <input type="radio" id="memGenderM" name="memGender" value="M" checked="checked">
                            <label for="memGenderM">남자&nbsp;</label>
                        </div>
                        <div class="icheck-primary d-inline">
                            <input type="radio" id="memGenderF" name="memGender" value="F">
                            <label for="memGenderF">여자 </label>
                        </div>
                    </div>
                </div>
                <div class="input-group mb-3">
                    <input type="text" class="form-control" id="memEmail" name="memEmail" placeholder="이메일을 입력해주세요">
                </div>
                <div class="input-group mb-3">
                    <input type="text" class="form-control" id="memPhone" name="memPhone" placeholder="전화번호를 입력해주세요">
                </div>
                <div class="input-group mb-3">
                    <input type="text" class="form-control" id="memPostCode" name="memPostCode">
                    <span class="input-group-append">
                        <button type="button" class="btn btn-secondary btn-flat" onclick="DaumPostcode()">우편번호 찾기</button>
                    </span>
                </div>
                <div class="input-group mb-3">
                    <input type="text" class="form-control" id="memAddress1" name="memAddress1" placeholder="주소를 입력해주세요">
                </div>
                <div class="input-group mb-3">
                    <input type="text" class="form-control" id="memAddress2" name="memAddress2" placeholder="상세주소를 입력해주세요">
                </div>
                <div class="row">
                    <div class="col-8">
                        <div class="icheck-primary">
                            <input type="checkbox" id="memAgree" name="memAgree" value="Y">
                            <label for="memAgree">개인정보처리방침</label>
                        </div>
                    </div>
                    <div class="col-4">
                        <button type="button" class="btn btn-dark btn-block" id="signupBtn">가입하기</button>
                    </div>
                    <button type="button" class="btn btn-secondary btn-block mt-4" onclick="javascript:location.href='/notice/login.do'">뒤로가기</button>
                </div>
            </form>
        </div>
    </div>
</div>
<script src="//t1.daumcdn.net/mapjsapi/bundle/postcode/prod/postcode.v2.js"></script>
<script>
	$(function(){
		// 로그인 페이지에서 사용할 배경 이미지 설정
		$("body").css("background-image", "url('${pageContext.request.contextPath }/resources/dist/img/background04.jpg')").css("background-size", "cover");
		
		var signupForm = $("#signupForm");		// 폼 체크 Element
		var signupBtn = $("#signupBtn");		// 가입하기 버튼 Element
		var idCheckBtn = $("#idCheckBtn");		// 중복확인 버튼 Element
		var idCheckFlag = false;				// 중복확인 flag
		
		var imgFile = $("#imgFile");			// 파일선택 Element
		
		idCheckBtn.on("click", function(){
			var id = $("#memId").val();
			
			if(!id) {
				alert("아이디를 입력해 주세요!");
				return false;
			}
			
			var data = {
				memId : id
			};
			
			$.ajax({
				type: "post",
				url: "/notice/idCheck.do",
				data: JSON.stringify(data),
				contentType: "application/json;charset=utf-8",
				success: function(res) {
					console.log("중복확인 후 넘겨받은 결과 : " + res);
					
					if(res == "NOTEXIST") { // 아이디 사용 가능
						alert("사용 가능한 아이디 입니다.");
						idCheckFlag = true; // 중복 확인 했다는 flag 설정
					}else { // 아이디 중복
						alert("이미 사용중인 아이디 입니다.");
						//idCheckFlag = false; // 중복 확인 했다는 flag 설정
					}
				}
			});
		});
		
		imgFile.on("change", function(event){
			var file = event.target.files[0];
			
			if(isImageFile(file)) {
				var reader = new FileReader();
				reader.onload = function(e){
					$("#profileImg").attr("src", e.target.result);
				}
				reader.readAsDataURL(file);
			}else { // 이미지 파일이 아닐 때
				alert("이미지 파일을 선택해주세요!");
			}
		});
		
		signupBtn.on("click", function(){
			
		});
	});
	
	// 이미지 파일인지 체크
	function isImageFile(file){
		var ext = file.name.split(".").pop().toLowerCase(); // 파일명에서 확장자를 가져옵니다.
		return ($.inArray(ext, ["jpg", "jpeg", "gif", "png"]) === -1) ? false : true;
	}
	
	// daum 주소 API(주소 찾기)
	function DaumPostcode() {
        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);
                    }
                }

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

                <div class="input-group mb-3">
                    <input type="text" class="form-control" id="memAddress1" name="memAddress1" placeholder="주소를 입력해주세요">
                </div>
                <div class="input-group mb-3">
                    <input type="text" class="form-control" id="memAddress2" name="memAddress2" placeholder="상세주소를 입력해주세요">
                </div>
                
                <!-- 상세 주소 선택시 그 안에 있는 주소를 합한 최종 결과의 주소를 가지고 정확한 위치의 지도를 뿌려볼 것이다. -->
                <div class="input-group mb-3">
                	<div id="map" style="width: 100%; height: 300px; display: none;"></div>
                </div>

https://apis.map.kakao.com/

https://apis.map.kakao.com/web/sample/addr2coord/

<script type="text/javascript" src="//dapi.kakao.com/v2/maps/sdk.js?appkey=발급받은 APP KEY를 사용하세요&libraries=services"></script>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<div class="register-box">
    <div class="card card-outline card-danger mt-4 mb-4">
        <div class="card-header text-center">
            <a href="" class="h1"><b>DDIT</b>BOARD</a>
        </div>
        <div class="card-body">
            <p class="login-box-msg">회원가입</p>

            <form action="/notice/signup.do" method="post" id="signupForm" enctype="multipart/form-data">
                <div class="input-group mb-3 text-center">
                    <img class="profile-user-img img-fluid img-circle" id="profileImg" src="${pageContext.request.contextPath }/resources/dist/img/AdminLTELogo.png" alt="User profile picture" style="width: 150px;">
                </div>
                <div class="input-group mb-3">
                    <label for="inputDescription">프로필 이미지</label>
                </div>
                <div class="input-group mb-3">
                    <div class="custom-file">
                        <input type="file" class="custom-file-input" name="imgFile" id="imgFile">
                        <label class="custom-file-label" for="imgFile">프로필 이미지를 선택해주세요</label>
                    </div>
                </div>
                <div class="input-group mb-3">
                    <label for="inputDescription">프로필 정보</label>
                </div>
                <div class="input-group mb-3">
                    <input type="text" class="form-control" id="memId" name="memId" placeholder="아이디를 입력해주세요">
                    <span class="input-group-append">
                        <button type="button" class="btn btn-secondary btn-flat" id="idCheckBtn">중복확인</button>
                    </span>
                    <span class="error invalid-feedback" style="display:block;"></span>
                </div>
                <div class="input-group mb-3">
                    <input type="text" class="form-control" id="memPw" name="memPw" placeholder="비밀번호를 입력해주세요">
                    <span class="error invalid-feedback" style="display:block;"></span>
                </div>
                <div class="input-group mb-3">
                    <input type="text" class="form-control" id="memName" name="memName" placeholder="이름을 입력해주세요">
                    <span class="error invalid-feedback" style="display:block;"></span>
                </div>
                <div class="input-group mb-3">
                    <div class="form-group clearfix">
                        <div class="icheck-primary d-inline">
                            <input type="radio" id="memGenderM" name="memGender" value="M" checked="checked">
                            <label for="memGenderM">남자&nbsp;</label>
                        </div>
                        <div class="icheck-primary d-inline">
                            <input type="radio" id="memGenderF" name="memGender" value="F">
                            <label for="memGenderF">여자 </label>
                        </div>
                    </div>
                </div>
                <div class="input-group mb-3">
                    <input type="text" class="form-control" id="memEmail" name="memEmail" placeholder="이메일을 입력해주세요">
                </div>
                <div class="input-group mb-3">
                    <input type="text" class="form-control" id="memPhone" name="memPhone" placeholder="전화번호를 입력해주세요">
                </div>
                <div class="input-group mb-3">
                    <input type="text" class="form-control" id="memPostCode" name="memPostCode">
                    <span class="input-group-append">
                        <button type="button" class="btn btn-secondary btn-flat" onclick="DaumPostcode()">우편번호 찾기</button>
                    </span>
                </div>
                <div class="input-group mb-3">
                    <input type="text" class="form-control" id="memAddress1" name="memAddress1" placeholder="주소를 입력해주세요">
                </div>
                <div class="input-group mb-3">
                    <input type="text" class="form-control" id="memAddress2" name="memAddress2" placeholder="상세주소를 입력해주세요">
                </div>
                
                <!-- 상세 주소 선택시 그 안에 있는 주소를 합한 최종 결과의 주소를 가지고 정확한 위치의 지도를 뿌려볼 것이다. -->
                <div class="input-group mb-3">
                	<div id="map" style="width: 100%; height: 300px; display: none;"></div>
                </div>
                
                <div class="row">
                    <div class="col-8">
                        <div class="icheck-primary">
                            <input type="checkbox" id="memAgree" name="memAgree" value="Y">
                            <label for="memAgree">개인정보처리방침</label>
                        </div>
                    </div>
                    <div class="col-4">
                        <button type="button" class="btn btn-dark btn-block" id="signupBtn">가입하기</button>
                    </div>
                    <button type="button" class="btn btn-secondary btn-block mt-4" onclick="javascript:location.href='/notice/login.do'">뒤로가기</button>
                </div>
            </form>
        </div>
    </div>
</div>
<script src="//t1.daumcdn.net/mapjsapi/bundle/postcode/prod/postcode.v2.js"></script>
<script type="text/javascript" src="//dapi.kakao.com/v2/maps/sdk.js?appkey=670a08a16143733e8641e4588bbc4ae6&libraries=services"></script>
<script>
	$(function(){
		// 로그인 페이지에서 사용할 배경 이미지 설정
		$("body").css("background-image", "url('${pageContext.request.contextPath }/resources/dist/img/background04.jpg')").css("background-size", "cover");
		
		var signupForm = $("#signupForm");		// 폼 체크 Element
		var signupBtn = $("#signupBtn");		// 가입하기 버튼 Element
		var idCheckBtn = $("#idCheckBtn");		// 중복확인 버튼 Element
		var idCheckFlag = false;				// 중복확인 flag
		
		var imgFile = $("#imgFile");			// 파일선택 Element
		
		var memAddress2 = $("#memAddress2");	// 상세주소 Element
		
		// kakaoMap API 사용 자원들
		var mapContainer = null;
		var map = null;
		var geocoder = null;
		
		idCheckBtn.on("click", function(){
			var id = $("#memId").val();
			
			if(!id) {
				alert("아이디를 입력해 주세요!");
				return false;
			}
			
			var data = {
				memId : id
			};
			
			$.ajax({
				type: "post",
				url: "/notice/idCheck.do",
				data: JSON.stringify(data),
				contentType: "application/json;charset=utf-8",
				success: function(res) {
					console.log("중복확인 후 넘겨받은 결과 : " + res);
					
					if(res == "NOTEXIST") { // 아이디 사용 가능
						alert("사용 가능한 아이디 입니다.");
						idCheckFlag = true; // 중복 확인 했다는 flag 설정
					}else { // 아이디 중복
						alert("이미 사용중인 아이디 입니다.");
						//idCheckFlag = false; // 중복 확인 했다는 flag 설정
					}
				}
			});
		});
		
		imgFile.on("change", function(event){
			var file = event.target.files[0];
			
			if(isImageFile(file)) {
				var reader = new FileReader();
				reader.onload = function(e){
					$("#profileImg").attr("src", e.target.result);
				}
				reader.readAsDataURL(file);
			}else { // 이미지 파일이 아닐 때
				alert("이미지 파일을 선택해주세요!");
			}
		});
		
		memAddress2.focusout(function(){
			var address1 = $("#memAddress1").val();
			var address2 = $("#memAddress2").val();
			
			if(!address1 || !address2) {
				// map 공간에 지도를 출력
				mapContainer = document.getElementById('map'), // 지도를 표시할 div 
				mapContainer.style.display = "block";
			    mapOption = {
			        center: new kakao.maps.LatLng(33.450701, 126.570667), // 지도의 중심좌표
			        level: 3 // 지도의 확대 레벨
			    };  

				// 지도를 생성합니다    
				map = new kakao.maps.Map(mapContainer, mapOption); 
	
				// 주소-좌표 변환 객체를 생성합니다
				geocoder = new kakao.maps.services.Geocoder();
	
				// 주소로 좌표를 검색합니다
				geocoder.addressSearch(address1 + " " + address2, function(result, status) {
	
				    // 정상적으로 검색이 완료됐으면 
				     if (status === kakao.maps.services.Status.OK) {
				        var coords = new kakao.maps.LatLng(result[0].y, result[0].x);
	
				        // 결과값으로 받은 위치를 마커로 표시합니다
				        var marker = new kakao.maps.Marker({
				            map: map,
				            position: coords
				        });
	
				        // 인포윈도우로 장소에 대한 설명을 표시합니다
				        var infowindow = new kakao.maps.InfoWindow({
				            content: '<div style="width:150px;text-align:center;padding:6px 0;">HOME</div>'
				        });
				        infowindow.open(map, marker);
	
				        // 지도의 중심을 결과값으로 받은 위치로 이동시킵니다
				        map.setCenter(coords);
				    } 
				    
				}); 
				
				$("#card-signup").css("top", "140px");
			}
			
		});
		
		signupBtn.on("click", function(){
			
		});
	});
	
	// 이미지 파일인지 체크
	function isImageFile(file){
		var ext = file.name.split(".").pop().toLowerCase(); // 파일명에서 확장자를 가져옵니다.
		return ($.inArray(ext, ["jpg", "jpeg", "gif", "png"]) === -1) ? false : true;
	}
	
	// daum 주소 API(주소 찾기)
	function DaumPostcode() {
        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);
                    }
                }

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

- http://localhost/notice/signup.do


		signupBtn.on("click", function(){
			// 아이디, 비밀번호, 이름까지만 일반적인 데이터 검증
			// 개인정보 처리방침 동의를 체크했을 때만 가입하기가 가능하도록
			// 중복확인 처리가 true 일때만 가입하기가 가능하도록
			var agreeFlag = false;
			var memId = $("#memId").val();
			var memPw = $("#memPw").val();
			var memName = $("#memName").val();
			
			if(!memId) {
				alert("아이디를 입력해 주세요!");
				return false;
			}
			
			if(!memPw) {
				alert("비밀번호를 입력해 주세요!");
				return false;
			}
			
			if(!memName) {
				alert("이름을 입력해 주세요!");
				return false;
			}
			
			// 개인정보 처리방침을 동의하게 되면 Y값이 넘어옴, 동의 여부는 true로 설정한다.
			var memAgree = $("#memAgree:checked").val();
			if(memAgree == "Y") {
				agreeFlag = true;
			}
			
			// 개인정보 처리방침을 동의했습니까?
			// 아이디 중복 체크를 이행하고 오셨습니까?
			// 오셨다구요? 그럼 가입하기를 진행하겠습니다.
			// 네? 안했다구요? 그럼 가입하기를 할 수 없습니다.
			if(agreeFlag){
				if(idCheckFlag) {
					signupForm.submit();
				}else {
					alert("아이디 중복체크를 해주세요!");
				}
			}else {
				alert("개인정보 동의를 체크해 주세요.");
			}
		});
	@RequestMapping(value = "/signup.do", method = RequestMethod.POST)
	public String signup(HttpServletRequest req, NoticeMemberVO memberVO, Model model, RedirectAttributes ra) {
		String goPage = "";
		
		Map<String, String> errors = new HashMap<String, String>();
		if(StringUtils.isBlank(memberVO.getMemId())) {
			errors.put("memId", "아이디를 입력해 주세요.");
		}
		if(StringUtils.isBlank(memberVO.getMemPw())) {
			errors.put("memId", "비밀번호를 입력해 주세요.");
		}
		if(StringUtils.isBlank(memberVO.getMemName())) {
			errors.put("memId", "이름을 입력해 주세요.");
		}
		
		if(errors.size() > 0) { // 넘겨받은 데이터의 에러가 존재
			model.addAttribute("errors", errors);
			model.addAttribute("member", memberVO);
			model.addAttribute("bodyText", "register-page");
			goPage = "conn/register";
		}else { // 정상적인 데이터를 받았을 때
			ServiceResult result = noticeService.signup(req, memberVO);
			if(result.equals(ServiceResult.OK)) { // 가입 성공
				ra.addFlashAttribute("message", "회원가입을 완료하였습니다!");
				goPage = "redirect:/notice/login.do";
			}else { // 가입 실패
				model.addAttribute("message", "서버에러, 다시 시도해 주세요!");
				model.addAttribute("member", memberVO);
				model.addAttribute("bodyText", "register-page");
				goPage = "conn/register";
			}
		}
		
		return goPage;
	}
package kr.or.ddit.service;

import java.util.List;

import javax.servlet.http.HttpServletRequest;

import kr.or.ddit.ServiceResult;
import kr.or.ddit.vo.crud.NoticeMemberVO;
import kr.or.ddit.vo.crud.NoticeVO;
import kr.or.ddit.vo.crud.PaginationInfoVO;

public interface INoticeService {

	public int selectNoticeCount(PaginationInfoVO<NoticeVO> pagingVO);
	public List<NoticeVO> selectNoticeList(PaginationInfoVO<NoticeVO> pagingVO);
	public ServiceResult insertNotice(NoticeVO noticeVO);
	public NoticeVO selectNotice(int boNo);
	public ServiceResult updateNotice(NoticeVO noticeVO);
	public ServiceResult deleteNotice(int boNo);
	
	public NoticeMemberVO loginCheck(NoticeMemberVO member);
	public ServiceResult idCheck(String memId);
	public ServiceResult signup(HttpServletRequest req, NoticeMemberVO memberVO);

}
package kr.or.ddit.service.impl;

import java.io.File;
import java.util.List;
import java.util.UUID;

import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;

import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import kr.or.ddit.ServiceResult;
import kr.or.ddit.mapper.ILoginMapper;
import kr.or.ddit.mapper.INoticeMapper;
import kr.or.ddit.service.INoticeService;
import kr.or.ddit.vo.crud.NoticeMemberVO;
import kr.or.ddit.vo.crud.NoticeVO;
import kr.or.ddit.vo.crud.PaginationInfoVO;

@Service
public class NoticeServiceImpl implements INoticeService {

	@Inject
	private INoticeMapper noticeMapper;
	@Inject
	private ILoginMapper loginMapper;

	@Override
	public int selectNoticeCount(PaginationInfoVO<NoticeVO> pagingVO) {
		return noticeMapper.selectNoticeCount(pagingVO);
	}

	@Override
	public List<NoticeVO> selectNoticeList(PaginationInfoVO<NoticeVO> pagingVO) {
		return noticeMapper.selectNoticeList(pagingVO);
	}

	@Override
	public ServiceResult insertNotice(NoticeVO noticeVO) {
		ServiceResult result = null;

		int status = noticeMapper.insertNotice(noticeVO);
		if (status > 0) {
			result = ServiceResult.OK;
		} else {
			result = ServiceResult.FAILED;
		}

		return result;
	}

	@Override
	public NoticeVO selectNotice(int boNo) {
		noticeMapper.incrementHit(boNo); // 게시글 조회수 증가
		return noticeMapper.selectNotice(boNo); // 게시글 번호에 해당하는 게시글 정보 가져오기
	}

	@Override
	public ServiceResult updateNotice(NoticeVO noticeVO) {
		ServiceResult result = null;

		int status = noticeMapper.updateNotice(noticeVO);
		if (status > 0) {
			result = ServiceResult.OK;
		} else {
			result = ServiceResult.FAILED;
		}

		return result;
	}

	@Override
	public ServiceResult deleteNotice(int boNo) {
		ServiceResult result = null;

		int status = noticeMapper.deleteNotice(boNo);
		if (status > 0) {
			result = ServiceResult.OK;
		} else {
			result = ServiceResult.FAILED;
		}

		return result;
	}

	@Override
	public NoticeMemberVO loginCheck(NoticeMemberVO member) {
		return loginMapper.loginCheck(member);
	}

	@Override
	public ServiceResult idCheck(String memId) {
		ServiceResult result = null;

		NoticeMemberVO member = loginMapper.idCheck(memId);
		if (member != null) {
			result = ServiceResult.EXIST;
		} else {
			result = ServiceResult.NOTEXIST;
		}

		return result;
	}

	@Override
	public ServiceResult signup(HttpServletRequest req, NoticeMemberVO memberVO) {
		ServiceResult result = null;

		// 회원가입 시, 프로필 이미지로 파일을 업로드 하는데 이때 업로드 할 서버 경로
		String uploadPath = req.getServletContext().getRealPath("/resources/profile");
		File file = new File(uploadPath);
		if (!file.exists()) {
			file.mkdirs();
		}

		String proFileImg = ""; // 회원정보에 추가될 프로필 이미지 경로
		try {
			// 넘겨받은 회원정보에서 파일 데이터 가져오기
			MultipartFile proFileImgFile = memberVO.getImgFile();

			// 넘겨받은 파일 데이터가 존재할 때
			if (proFileImgFile.getOriginalFilename() != null && !proFileImgFile.getOriginalFilename().equals("")) {
				String fileName = UUID.randomUUID().toString(); // UUID 파일명 생성
				fileName += "_" + proFileImgFile.getOriginalFilename(); // UUID_원본파일명으로 파일명 생성
				uploadPath += "/" + fileName; // /resources/profile/uuid_원본파일명

				proFileImgFile.transferTo(new File(uploadPath)); // 해당 위치에 파일 복사
				proFileImg = "/resources/profile/" + fileName; // 파일 복사가 일어난 파일의 위치로 접근하기 위한 URI 설정
			}

			memberVO.setMemProfileImg(proFileImg);
		} catch (Exception e) {
			e.printStackTrace();
		}

		int status = loginMapper.signup(memberVO);

		if (status > 0) { // 등록 성공
			result = ServiceResult.OK;
		} else {
			result = ServiceResult.FAILED;
		}

		return result;
	}

}
package kr.or.ddit.mapper;

import kr.or.ddit.vo.crud.NoticeMemberVO;

public interface ILoginMapper {

	public NoticeMemberVO loginCheck(NoticeMemberVO member);
	public NoticeMemberVO idCheck(String memId);
	public int signup(NoticeMemberVO memberVO);

}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="kr.or.ddit.mapper.ILoginMapper">
	
	<!-- ctrl + shift + y : 소문자 변환 -->
	<select id="loginCheck" parameterType="noticeMemberVO" resultType="noticeMemberVO">
		select 
			mem_no,
			mem_id,
			mem_pw,
			mem_name,
			mem_gender,
			mem_email,
			mem_phone,
			mem_postcode,
			mem_address1,
			mem_address2,
			mem_agree,
			mem_profileimg,
			mem_regdate
		from noticemember 
		where mem_id = #{memId} 
		and mem_pw = #{memPw}
	</select>
	
	<select id="idCheck" parameterType="string" resultType="noticeMemberVO">
		select 
			mem_no,
			mem_id,
			mem_pw,
			mem_name,
			mem_gender,
			mem_email,
			mem_phone,
			mem_postcode,
			mem_address1,
			mem_address2,
			mem_agree,
			mem_profileimg,
			mem_regdate
		from noticemember 
		where mem_id = #{memId} 
	</select>
	
	<insert id="signup" parameterType="noticeMemberVO">
		insert into noticemember (
			mem_no,
			mem_id,
			mem_pw,
			mem_name,
			mem_gender,
			mem_email,
			mem_phone,
			mem_postcode,
			mem_address1,
			mem_address2,
			mem_agree,
			mem_profileimg,
			mem_regdate,
			enabled
		) values (
			seq_noticemember.nextval,
			#{memId},
			#{memPw},
			#{memName},
			#{memGender},
			#{memEmail},
			#{memPhone},
			#{memPostCode},
			#{memAddress1},
			#{memAddress2},
			#{memAgree},
			#{memProfileImg},
			sysdate,
			1
		)
	</insert>
	
</mapper>

- http://localhost/notice/signup.do

 


 

'대덕인재개발원 > 대덕인재개발원_웹기반 애플리케이션' 카테고리의 다른 글

231206_SPRING 2 (11-1)  (1) 2023.12.06
231205_SPRING 2 (10-2)  (0) 2023.12.05
231204_SPRING 2 (9-3)  (0) 2023.12.04
231204_SPRING 2 (9-2)  (0) 2023.12.04
231204_SPRING 2 (9-1)  (0) 2023.12.04