TIL 0626

먼지·2024년 6월 26일

Today I Learned

목록 보기
85/89
post-thumbnail

Ch 15 Spring Page

resources > message

Validation.properties

각각의 에러코드에 메세지 넣어주기

#회원 관리
Pattern.id = 영문, 숫자만 입력 최소 4자~ 최대 12자
NotBlank.name = 이름은 필수 항목
NotBlank.passwd = 비밀번호는 필수 항목
Pattern.passwd = 영문, 숫자만 입력 최소 4자~최대 12자
Pattern.now_passwd=영문,숫자만 입력 최소 4자~ 최대 12자
NotBlank.email = 이메일은 필수 항목
Size.zipcode = 우편번호는 5자리
NotBlank.address1 = 주소는 필수 항목
NotBlank.address2 = 상세 주소는 필수 항목
invalidIdOrPassword = 아이디 또는 비밀번호 불일치
noAuthority = 정지 회원입니다.

kr.spring.member.dao

Member Mapper

package kr.spring.member.dao;

import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;

import kr.spring.member.vo.MemberVO;

@Mapper
public interface MemberMapper {
	
	// 회원 관리 - 일반 회원
	@Select("SELECT spmember_seq.nextval FROM dual")
	public Long selectMem_num();
	@Insert("INSERT INTO spmember (mem_num, id, nick_name, auth) VALUES (#{mem_num}, #{id}, #{nick_name}, 2)")
	public void insertMember(MemberVO member);
	// xml
	public void insertMember_detail(MemberVO member);
	// xml
	public MemberVO selectCheckMember(String id);
	@Select("SELECT * FROM spmember JOIN spmember_detail USING(mem_num) WHERE mem_num=#{mem_num}")
	public MemberVO selectMember(Long mem_num);
	@Update("UPDATE spmember SET nick_name=#{nick_name} WHERE mem_num=#{mem_num}")
	public void updateMember(MemberVO member);
    // xml
	public void updateMember_detail(MemberVO member);
	public void updatePassword(MemberVO member);
	public void deleteMember(Long mem_num);
	public void deleteMember_detail(Long mem_num);
}

Member Mapper.xml

로그인 처리 & 아이디 중복 확인에 사용

selectCheckMember
현재 구문이 사용될 Member Mapper Interface에서 사용되는 메서드 명을 작성해준다.
parameterType="string"
ID 타입이 String 타입이기 때문에 알리아스 형태인 string을 작성하면 된다.
resultType="memberVO"
결과가 자바빈에 담겨있기 때문에 자바빈을 작성해준다.

SQL 구문에서 Left Outer Join을 사용하여 spmember에 spmember_detail을 Join시켜주는데 회원이 탈퇴 할 때 spmember_detail의 데이터만 삭제하기 때문에 누락되는 데이터가 생길 수 있어 Join 대신 Left Outer Join을 사용한다.

	<select id="selectCheckMember" parameterType="string" resultType="memberVO">
		SELECT mem_num, id, auth, nick_name, au_id, passwd, email 
		FROM spmember LEFT OUTER JOIN spmember_detail USING(mem_num) 
		WHERE id = #{id}
	</select>

회원 정보 수정

회원의 세부 정보를 수정하는 SQL 구문
전달되는 값이 자바빈이기 때문에 memberVO가 parameterType에 들어간다.

	<update id="updateMember_detail" parameterType="memberVO">
	UPDATE spmember_detail SET name=#{name}, 
	phone=#{phone}, 
	email=#{email}, 
	zipcode=#{zipcode}, 
	address1=#{address1}, 
	address2=#{address2}, 
	modify_date=SYSDATE 
	WHERE mem_num=#{mem_num}
	</update>

전체 코드

<?xml version="1.0" encoding="UTF-8" ?>  
<!DOCTYPE mapper   
    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"   
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">  

<!-- interface명칭과 xml의 명칭이 동일해야 한다 -->
<mapper namespace="kr.spring.member.dao.MemberMapper">
	<insert id="insertMember_detail" parameterType="memberVO">
    INSERT INTO spmember_detail (
        mem_num, name, passwd, phone, email, zipcode, address1, address2
    ) VALUES (
        #{mem_num},
        #{name}, #{passwd}, #{phone}, #{email}, #{zipcode}, #{address1}, #{address2}
    )
	</insert>
	
	<!-- 아이디 중복 체크 -->			
	<select id="selectCheckMember" parameterType="string" resultType="memberVO">
		SELECT mem_num, id, auth, nick_name, au_id, passwd, email 
		FROM spmember LEFT OUTER JOIN spmember_detail USING(mem_num) 
		WHERE id = #{id}
	</select>

	<!-- 회원 정보 수정 -->
	<update id="updateMember_detail" parameterType="memberVO">
	UPDATE spmember_detail SET name=#{name}, 
	phone=#{phone}, 
	email=#{email}, 
	zipcode=#{zipcode}, 
	address1=#{address1}, 
	address2=#{address2}, 
	modify_date=SYSDATE 
	WHERE mem_num=#{mem_num}
	</update>
 </mapper>

WEB-INF > tiles-def

Member.xml

회원 로그인

회원의 로그인은 메인과 같은 layout을 사용하고 있기 때문에 main을 상속 받고 있다.
그래서 동일하지 않은 속성만 지정을 해주고 작성하지 않은 부분의 main의 내용을 그대로 받아온다.

<definition name="memberLogin" extends="main">
		<put-attribute name="title" value="Login Page"/>
		<put-attribute name="css" value="/WEB-INF/views/member/memberCSS.jsp" />
		<put-attribute name="body" value="/WEB-INF/views/member/memberLogin.jsp" />
</definition>

My Page

마이페이지는 메인과 동일한 layout아닌 mypage 레이아웃을 따로 지정하여 사용하기 때문에 직접 title, header, nav, css, body, footer을 작성해줘야 한다.

<definition name="myPage" template="/WEB-INF/views/template/layout_mypage.jsp">
		<put-attribute name="title" value="My Page"/>
		<put-attribute name="header" value="/WEB-INF/views/template/header.jsp" />
		<put-attribute name="nav" value="/WEB-INF/views/template/nav_mypage.jsp" />
		<put-attribute name="css" value="/WEB-INF/views/member/memberCSS.jsp" />
		<put-attribute name="body" value="/WEB-INF/views/member/memberView.jsp" />
		<put-attribute name="footer" value="/WEB-INF/views/template/footer.jsp" />
</definition>

전체 코드

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

<!DOCTYPE tiles-definitions PUBLIC
       "-//Apache Software Foundation//DTD Tiles Configuration 3.0//EN"
       "http://tiles.apache.org/dtds/tiles-config_3_0.dtd">

<tiles-definitions>
	<!-- 기본 레이아웃 설정 -->
	<definition name="memberRegister" extends="main">
		<put-attribute name="title" value="Spring Page Register"/>
		<put-attribute name="css" value="/WEB-INF/views/member/memberCSS.jsp" />
		<put-attribute name="body" value="/WEB-INF/views/member/memberRegister.jsp" />
	</definition>
	
	<definition name="memberLogin" extends="main">
		<put-attribute name="title" value="Login Page"/>
		<put-attribute name="css" value="/WEB-INF/views/member/memberCSS.jsp" />
		<put-attribute name="body" value="/WEB-INF/views/member/memberLogin.jsp" />
	</definition>
	
	<!--  MyPage -->
	<definition name="myPage" template="/WEB-INF/views/template/layout_mypage.jsp">
		<put-attribute name="title" value="My Page"/>
		<put-attribute name="header" value="/WEB-INF/views/template/header.jsp" />
		<put-attribute name="nav" value="/WEB-INF/views/template/nav_mypage.jsp" />
		<put-attribute name="css" value="/WEB-INF/views/member/memberCSS.jsp" />
		<put-attribute name="body" value="/WEB-INF/views/member/memberView.jsp" />
		<put-attribute name="footer" value="/WEB-INF/views/template/footer.jsp" />
	</definition>
	
</tiles-definitions>

kr.spring.member.service

Member Service Impl

package kr.spring.member.service;

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

import kr.spring.member.dao.MemberMapper;
import kr.spring.member.vo.MemberVO;

@Service
@Transactional
public class MemberServiceImpl implements MemberService{
	
	@Autowired
	private MemberMapper memberMapper;
	
	@Override
	public void insertMember(MemberVO member) {
		// 여기서 insert 문들을 모두 조합해서 정보를 전달
		member.setMem_num(memberMapper.selectMem_num());
		memberMapper.insertMember(member);
		memberMapper.insertMember_detail(member);
	}

	@Override
	public MemberVO selectCheckMember(String id) {
		return memberMapper.selectCheckMember(id);
	}

	@Override
	public MemberVO selectMember(Long mem_num) {
		return memberMapper.selectMember(mem_num);
	}

	@Override
	public void updateMember(MemberVO member) {
		memberMapper.updateMember(member);
		memberMapper.updateMember_detail(member);
	}

	@Override
	public void updatePassword(MemberVO member) {
		
	}

	@Override
	public void deleteMember(Long mem_num) {
		
	}

}

kr.spring.util

AuthCheckException

회원 로그인 처리에 필요한 예외 처리

package kr.spring.util;

public class AuthCheckException extends Exception{

}

kr.spring.interceptor

LoginCheckInterceptor

LoginCheckInterceptor 클래스는 HandlerInterceptor 인터페이스를 구현하여 인터셉터 기능을 제공한다.

로그인을 처리할 때 로그인하면 해당 정보를 user라는 객체에 저장을 했기 때문에 로그인이 되어있는지 안 되어있는지 user의 속성을 확인하여 로그인 여부를 검사한다.
user 속성이 null이면 로그인되지 않은 상태로 간주하고 로그인 페이지로 리다이렉트한다.
user 속성이 존재하면 로그인된 상태로 간주하고 요청을 계속 처리한다.

false의 값이 반환된다면 로그인이 안 된 상태(요청한 URL을 호출하지 않은 경우)
true의 값이 반환된다면 로그인이 된 상태 (요청한 URL을 호출한 경우)

package kr.spring.interceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.springframework.web.servlet.HandlerInterceptor;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class LoginCheckInterceptor  implements HandlerInterceptor{
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
		
		log.debug("<<Login Check Interceptor 진입>>");
		
		HttpSession session = request.getSession();
		
		// 로그인 여부 검사
		if(session.getAttribute("user")==null) {
			// 로그인 되지 않은 상태
			 response.sendRedirect(request.getContextPath() + "/member/login");
			 
			 return false;
		}
		log.debug("<< Login Check Interceptor 로그인이 된 상태! >>");
		return true; // 요청한 URL을 호출할 경우 true -> 로그인이 된 상태다
	}
}

kr.spring.config

AppConfig

코드 뜯어 보기

LoginCheckInterceptor 객체를 생성하고 스프링 빈으로 등록한다. 스프링이 이 인터셉터를 관리하고 사용할 수 있다.

@Bean
	public LoginCheckInterceptor interceptor2() {
		loginCheck = new LoginCheckInterceptor();
		return loginCheck;
	}

InterceptorRegistry에 인터셉터를 등록한다.
addPathPatterns를 사용하여 인터셉터를 적용할 경로를 지정한다. /member/myPage와 /member/update 경로에 대해 인터셉터를 적용한다. 회원제 서비스마다 등록을 해준다고 생각하면 쉽다. 해당 페이지가 너무 많다면 * 을 이용해서 작성할 수 있다.

@Override
public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(loginCheck)
            .addPathPatterns("/member/myPage")
            .addPathPatterns("/member/update");
}

전체 코드

package kr.spring.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.view.tiles3.TilesConfigurer;
import org.springframework.web.servlet.view.tiles3.TilesView;
import org.springframework.web.servlet.view.tiles3.TilesViewResolver;

import kr.spring.interceptor.LoginCheckInterceptor;

// 자바코드 기반 설정 클래스
@Configuration
public class AppConfig implements WebMvcConfigurer{
	
	private LoginCheckInterceptor loginCheck;
	
	@Bean
	public LoginCheckInterceptor interceptor2() {
		loginCheck = new LoginCheckInterceptor();
		return loginCheck;
	}
	
	@Override
	public void addInterceptors(InterceptorRegistry registry) {
		registry.addInterceptor(loginCheck).addPathPatterns("/member/myPage")
										   .addPathPatterns("/member/update");
	}
	
	@Bean
	public TilesConfigurer tilesConfigurer() {
		final TilesConfigurer configurer = new TilesConfigurer();
		
		// XML 설정 파일 경로 지정 -> 배열이기 때문에 다양한 파일 지정 가능함
		configurer.setDefinitions(new String[] {
				"/WEB-INF/tiles-def/main.xml",
				"/WEB-INF/tiles-def/member.xml"
		});
		configurer.setCheckRefresh(true);
		return configurer;
	}
	
	@Bean
	public TilesViewResolver tilesViewResolver() {
		final TilesViewResolver tilesViewResolver = new TilesViewResolver();
		tilesViewResolver.setViewClass(TilesView.class);
		
		return tilesViewResolver;
	}
}

kr.spring.member.controller

Member Ajax Controller

Ajax를 처리하는 Controller

코드 뜯어보기

@ResponseBody
메서드의 반환 값을 JSON 형식으로 HTTP 응답 본문에 직접 쓰기 위해 사용

@RequestParam String id
요청 파라미터로 전달된 id 값을 받는다. 만약 id 파라미터가 전달되지 않으면 오류가 발생한다.

log.debug("<<아이디 중복 체크>> : " + id);
Map<String, String> mapAjax = new HashMap<String, String>();
MemberVO member = memberService.selectCheckMember(id);

로그를 통해 입력된 아이디를 확인한다. 결과를 담을 mapAjax라는 HashMap 객체를 생성한다.
memberService.selectCheckMember(id) 메서드를 호출하여 해당 아이디가 데이터베이스에 존재하는지 확인한다. 존재한다면 MemberVO 객체가 반환되고, 존재하지 않으면 null이 반환된다.

if (member != null) {
    mapAjax.put("result", "idDuplicated");
} else {
    if (!Pattern.matches("^[a-zA-Z0-9]{4,12}$", id)) {
        mapAjax.put("result", "notMatchPattern");
    } else {
        mapAjax.put("result", "idNotFound");
    }
}
return mapAjax;

아이디가 존재하는 경우(아이디 중복인 경우) mapAjax에 idDuplicated 결과 추가
아이디가 존재하지 않으면, 정규 표현식을 사용하여 아이디의 형식에 맞게 작성이 되었는지 확인한다.
형식이 맞지 않으면 notMatchPattern 결과 추가
형식이 맞고 아이디가 중복되지 않으면 idNotFound 결과를 추가한다.
최종적으로 mapAjax를 반환하여 JSON 형식의 응답을 생성한다.

전체 코드

package kr.spring.member.controller;

import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import kr.spring.member.service.MemberService;
import kr.spring.member.vo.MemberVO;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Controller
public class MemberAjaxController {
	@Autowired
	private MemberService memberService;
	
	// ajax는 list 또는 map으로 반환
	// 값이 전달이 안된다면 오류가 나도록 requestparam 작성
	@GetMapping("/member/confirmId")
	@ResponseBody
	public Map<String,String> process(@RequestParam String id){
		log.debug("<<아이디 중복 체크>> : " + id);
		
		Map<String, String> mapAjax = new HashMap<String, String>();
		
		MemberVO member = memberService.selectCheckMember(id);
		
		if(member != null) {
			// 아이디 중복
			mapAjax.put("result", "idDuplicated");
		} else {
			if(!Pattern.matches("^[a-zA-Z0-9]{4,12}$",id)) {
				mapAjax.put("result", "notMatchPattern");
			} else {
//				패턴이 일치하면서 아이디 미중복
				mapAjax.put("result", "idNotFound");
			}
		}
		
		return mapAjax;
	}
}

Member Controller

로그인 폼 호출하기

	@GetMapping("/member/login")
	public String formLogin() {
		return "memberLogin";
	}
	

로그인 처리하기

  1. 유효성 체크 진행
  • id와 passwd 필드에 유효성 오류가 있는지 확인
  • 오류가 있다면 로그인 폼을 다시 호출
  1. memberService.selectCheckMember(memberVO.getId())
    주어진 ID로 멤버 정보를 조회한다. 여기서 memberVO 는 사용자가 입력한 값(로그인 하려는 ID)을 의미한다.

    if(member != null) { // 아이디 존재함
    			// 비밀번호 일치 여부 체크
    			check = member.ischeckedPassword(memberVO.getPasswd());
    		}

    아이디가 존재하는 경우 if 구문으로 들어가서 비밀번호 일치 여부를 체크한다.

  2. check가 true로 반환되면 if문에 진입한다.

    if(check) { 
    			session.setAttribute("user", member);
    			
    			log.debug("<< 인증 성공! >>");
    			log.debug("<< ID >> : " + member.getId());
    			log.debug("<< Au_ID >> : " + member.getAu_id());
    			log.debug("<< Auth >> : " + member.getAuth());
    			
    			if(member.getAuth() == 9) {
    				return"redirect:/main/admin";
    			} else {
    				return"redirect:/main/main";
    			}
    		}  

    인증이 성공이 된 경우 세션에 사용자 정보를 user라는 이름으로 저장한다.
    로그를 출력시킨다. 만약 관리자의 경우는 관리자의 메인 화면으로 리다이렉트, 일반 회원의 경우 메인 페이지로 리다이렉트 된다.

  3. 만약 check가 false 값이 반환이 된다면 AuthCheckException의 예외를 발생시킨다.

	throw new AuthCheckException();
  1. AuthCheckException 을 처리하여 인증 실패 시 적절한 에러 메시지를 설정한다.
    formLogin() 을 호출하여 로그인 폼을 다시 호출시킨다.
    catch (AuthCheckException e) {
    		// 인증 실패 로그인 폼 호출하기
    		
    		if(member != null && member.getAuth() == 1) {
    			// 정지회원 메세지 표시하기
    			result.reject("noAuthority");
    		} else {
    			result.reject("invalidIdOrPassword");
    		}
    		log.debug("<< 인증에 실패하였습니다. >>");
    	}
    	return formLogin();	

로그아웃 처리하기

HttpSession session 현재 세션을 나타내는 객체
현재 세션을 무효화하여 사용자를 로그아웃시키고 세션에 저장된 모든 정보를 제거한다.

@GetMapping("/member/logout")
	public String processLogout(HttpSession session) {
		// 로그아웃 처리
		session.invalidate();
		
		// 자동 로그인 처리 해제 시작
		
		// 자동 로그인 처리 해제 종료
		
		log.debug("<< 로그아웃이 완료되었습니다! >>");
		return "redirect:/main/main";
	}

마이페이지 호출

  1. 현재 세션에서 user라는 이름으로 저장된 MemberVO 객체를 가져온다.
    사용자가 로그인하지 않았다면 user는 null일 수 있습니다.

  2. memberService.selectMember(user.getMem_num())
    메서드를 호출하여 데이터베이스에서 해당 사용자의 상세 정보를 조회
    조회된 회원 정보를 member 객체에 저장한다.

  3. model 객체에 member라는 이름으로 조회된 회원 정보를 추가한다.
    뷰에서 이 데이터를 사용할 수 있도록 한다.

@GetMapping("/member/myPage")
	public String process(HttpSession session, Model model) {
		MemberVO user = (MemberVO)session.getAttribute("user");
		MemberVO member = memberService.selectMember(user.getMem_num());
		log.debug("<< My Page >> : " + member);
		
		model.addAttribute("member",member);
		return "myPage";
	}

전체 코드

package kr.spring.member.controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.validation.Valid;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;

import kr.spring.member.service.MemberService;
import kr.spring.member.vo.MemberVO;
import kr.spring.util.AuthCheckException;

@Controller
public class MemberController {
	
	@Autowired
	public MemberService memberService;
	
	private static final Logger log = LoggerFactory.getLogger(MemberController.class);
	
	// 회원 가입 - 자바빈 초기화
	@ModelAttribute
	public MemberVO initCommand() {
		return new MemberVO();
	}
	
	// 회원 가입 - 폼 호출
	@GetMapping("/member/registerUser")
	public String form() {
		return "memberRegister";
	}
	
	@PostMapping("/member/registerUser")
	public String submit(@Valid MemberVO memberVO, BindingResult result, Model model, HttpServletRequest request) {
		log.debug("<<회원 가입>> : " + memberVO);
		
		if(result.hasErrors()) {
			return form();
		}
		
		memberService.insertMember(memberVO);
		
		//  UI 문구 처리
		model.addAttribute("accessTitle","회원 가입");
		model.addAttribute("accessMsg","회원 가입이 완료되었습니다");
		model.addAttribute("accessBtn","홈으로");
		model.addAttribute("accessUrl", request.getContextPath()+"/main/main");
		
		return "common/resultView";
	}
	
	// 회원 로그인 폼 호출하기
	@GetMapping("/member/login")
	public String formLogin() {
		return "memberLogin";
	}
	
	// 회원 로그인 처리하기
	@PostMapping("/member/login")
	public String submitLogin(@Valid MemberVO memberVO, BindingResult result, HttpSession session,HttpServletResponse response ) {
		
		log.debug("<< 로그인 >> : " + memberVO);
		
		// 유효성 체크 결과 오류가 있다면 form 호출
		if(result.hasFieldErrors("id") || result.hasFieldErrors("passwd")) {
			return formLogin();
		}
		
		// 아이디 비밀번호 일치 여부 체크하기
		MemberVO member = null;
		
		try {
			member = memberService.selectCheckMember(memberVO.getId());
			
			boolean check = false;
			
			if(member != null) { // 아이디 존재함
				// 비밀번호 일치 여부 체크
				check = member.ischeckedPassword(memberVO.getPasswd());
			}
			// true 라면 if(check) 내부로 진입함
			if(check) { // 인증 성공
				// 자동 로그인 체크 시작
				
				// 자동 로그인 체크 종료
				
				// 멤버 확인하여 로그인 처리
				// session에다가 자바빈을 통채로 집어넣기
				session.setAttribute("user", member);
				
				log.debug("<< 인증 성공! >>");
				log.debug("<< ID >> : " + member.getId());
				log.debug("<< Au_ID >> : " + member.getAu_id());
				log.debug("<< Auth >> : " + member.getAuth());
				
				if(member.getAuth() == 9) {
					// 관리자라면
					return"redirect:/main/admin";
				} else {
					return"redirect:/main/main";
				}
			}
			// 인증 실패
			throw new AuthCheckException();
		} 
		catch (AuthCheckException e) {
			// 인증 실패 로그인 폼 호출하기
			
			if(member != null && member.getAuth() == 1) {
				// 정지회원 메세지 표시하기
				result.reject("noAuthority");
			} else {
				result.reject("invalidIdOrPassword");
			}
			log.debug("<< 인증에 실패하였습니다. >>");
		}
		return formLogin();	
	}
	
	// 로그아웃 처리하기
	@GetMapping("/member/logout")
	public String processLogout(HttpSession session) {
		// 로그아웃 처리
		session.invalidate();
		
		// 자동 로그인 처리 해제 시작
		
		// 자동 로그인 처리 해제 종료
		
		log.debug("<< 로그아웃이 완료되었습니다! >>");
		return "redirect:/main/main";
	}
	
	@GetMapping("/member/myPage")
	public String process(HttpSession session, Model model) {
		// 로그인 한 사람의 회원 정보(session)를 가져온다
		MemberVO user = (MemberVO)session.getAttribute("user");
		
		MemberVO member = memberService.selectMember(user.getMem_num());
		log.debug("<< My Page >> : " + member);
		
		model.addAttribute("member",member);
		return "myPage";
	}
		
}

resources > static > js

jqueyr-3.7.1.min.js 추가하기

member.register.js

$(function(){
	/**
 	*  회원 가입
 	*/
	// 아이디 중복 여부 저장 변수
	let checkId = 0;
	// 0: 아이디 중복 또는 체크 미실행
	// 1: 아이디 미중복
	
	// 아이디 중복 체크
	 $('#confirmId').click(function(){
		if($('#id').val().trim() == ''){
			$('#message_id').css('color','red').text('아이디를 입력하세요');
			$('#id').val('').focus();
			return;
		}
		
		$('#message_id').text('');	// 알림 메세지 초기화
		
		// 서버와 통신하기
		// url: 같은 경로 url 명시
		$.ajax({
			url:'confirmId',
			type:'get',
			data:{id:$('#id').val()},
			dataType: 'json',
			success:function(param){
				if(param.result == `idDuplicated`){
					checkId = 0;
					$('#message_id').css('color','red').text('중복된 아이디');
					$('#id').val('').focus();
				}
				else if(param.result == `notMatchPattern`){
					checkId = 0;
					$('#message_id').css('color','red').text('잘못된 형식');
					$('#id').val('').focus();
				}
				else if(param.result == 'idNotFound'){
					checkId = 1;
					$('#message_id').css('color','#000').text('등록 가능한 ID');
				} else{
					checkId = 0;
					alert('ID 중복 체크 오류가 발생하였습니다.')
				}
			},
			error:function(){
				checkId = 0;
				alert('네트워크 오류가 발생하였습니다.');
			}
			
		}); // end of ajax
	 }); // end of click
	
	// 아이디 중복 안내 메세지 초기화 및 아이디 중복 값 초가화
	$('#member_register #id').keydown(function(){
		checkId = 0;
		$('#message_id').text('');
	}); // end of keydown
	
	$('#member_register').submit(function(){
		if(checkId == 0){
			$('#message_id').css('color','red').text('ID 중복 체크는 필수');
			
			if($('#id').val().trim()==''){
				$('#id').val('').focus();
			}
			return false;
		}
	});	// end of submit
	
	
}); // end of function

WEB-INF > views > template

Header.jsp

로그인 했을 때 설정 추가하기

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!--  상단 시작 -->
<h2 class="align-center">Spring Page</h2>
<div class="align-right">
	<!-- 자바빈을 통채로 넣어 확인하기 -->
	<!-- 로그인이 되어있는 경우 -->
	<c:if test="${!empty user}">
		<a href="${pageContext.request.contextPath}/member/myPage" style="margin-right: 2%;">${user.id}</a>
		<a href="${pageContext.request.contextPath}/member/logout" style="margin-right: 2%;">로그아웃</a>
	</c:if>
	<!-- 로그인이 안 되어 있는 경우 -->
	<c:if test="${empty user}">
		<a href="${pageContext.request.contextPath}/member/registerUser" style="margin-right: 2%;">회원가입</a>
		<a href="${pageContext.request.contextPath}/member/login" style="margin-right: 2%;">로그인</a>
	</c:if>
		<a href="${pageContext.request.contextPath}/main/main" style="margin-right: 2%;"></a>

</div>
<!--  상단 끝 -->

WEB-INF > views > member

memberRegister.jsp

id 잘못된 부분이 있어서 수정하고, 아이디 중복 체크를 위해 JS 추가해줌

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>

<!-- 회원 가입 시작 -->
<div class="page-main">
	<h2>회원 가입</h2>
	<form:form action="registerUser" id="member_register" modelAttribute="memberVO">
		<ul>
			<li>
				<form:label path="id">아이디</form:label>
				<form:input path="id" placeholder="영문, 숫자만 가능 최소 4자~ 최대 12자까지" autocomplete="off"/>
				<input type="button" id="confirmId" value="ID 중복 확인" class="default-btn">
				<span id="message_id"></span>
				<form:errors element="div" path="id" cssClass="error-color-reg"/>
			</li>
			
			<li>
				<form:label path="name">이름</form:label>
				<form:input path="name" />
				<form:errors element="div" path="name" cssClass="error-color-reg"/>
			</li>
			
			<li>
				<form:label path="nick_name">닉네임</form:label>
				<form:input path="nick_name"/>
			</li>
			
			<li>
				<form:label path="passwd">비밀번호</form:label>
				<form:password path="passwd" placeholder="영문, 숫자만 가능 최소 4자~ 최대 12자까지"/>
				<form:errors element="div" path="passwd" cssClass="error-color-reg"/>
			</li>
			
			<li>
				<form:label path="phone">전화 번호</form:label>
				<form:input path="phone" placeholder="010********" />
				<form:errors element="div" path="phone" cssClass="error-color-reg"/>
			</li>
			
			<li>
				<form:label path="email">이메일</form:label>
				<form:input path="email"/>
				<form:errors element="div" path="email" cssClass="error-color-reg"/>
			</li>
			
			<li>
				<form:label path="zipcode">우편번호</form:label>
				<form:input path="zipcode"/>
				<input type="button" onclick="execDaumPostcode()" value="우편번호 찾기" class="default-btn">
				<form:errors element="div" path="zipcode" cssClass="error-color-reg"/>
			</li>
			
			<li>
				<form:label path="address1">주소</form:label>
				<form:input path="address1"/>
				<form:errors element="div" path="address1" cssClass="error-color-reg"/>
			</li>
			
			<li>
				<form:label path="address2">상세 주소</form:label>
				<form:input path="address2"/>
				<form:errors element="div" path="address2" cssClass="error-color-reg"/>
			</li>
		</ul>
		<div class="align-center">
			<form:button class="default-btn">전송</form:button>
			<input type="button" value="홈으로"
			  class="default-btn"
			  onclick="location.href='${pageContext.request.contextPath}/main/main'">
		</div>   
	</form:form>
	
	<!-- iOS에서는 position:fixed 버그가 있음, 적용하는 사이트에 맞게 position:absolute 등을 이용하여 top,left값 조정 필요 -->
<div id="layer" style="display:none;position:fixed;overflow:hidden;z-index:1;-webkit-overflow-scrolling:touch;">
<img src="//t1.daumcdn.net/postcode/resource/images/close.png" id="btnCloseLayer" style="cursor:pointer;position:absolute;right:-3px;top:-3px;z-index:1" onclick="closeDaumPostcode()" alt="닫기 버튼">
</div>

<script src="//t1.daumcdn.net/mapjsapi/bundle/postcode/prod/postcode.v2.js"></script>
<script type="text/javascript" src="${pageContext.request.contextPath}/js/jquery-3.7.1.min.js"></script>
<script type="text/javascript" src="${pageContext.request.contextPath}/js/member.register.js"></script>
<script>
    // 우편번호 찾기 화면을 넣을 element
    var element_layer = document.getElementById('layer');

    function closeDaumPostcode() {
        // iframe을 넣은 element를 안보이게 한다.
        element_layer.style.display = 'none';
    }

    function 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 + ')';
                    }
                    //(주의)address1에 참고항목이 보여지도록 수정
                    // 조합된 참고항목을 해당 필드에 넣는다.
                    //(수정) document.getElementById("address2").value = extraAddr;
                
                } 
                //(수정) else {
                //(수정)    document.getElementById("address2").value = '';
                //(수정) }

                // 우편번호와 주소 정보를 해당 필드에 넣는다.
                document.getElementById('zipcode').value = data.zonecode;
                //(수정) + extraAddr를 추가해서 address1에 참고항목이 보여지도록 수정
                document.getElementById("address1").value = addr + extraAddr;
                // 커서를 상세주소 필드로 이동한다.
                document.getElementById("address2").focus();

                // iframe을 넣은 element를 안보이게 한다.
                // (autoClose:false 기능을 이용한다면, 아래 코드를 제거해야 화면에서 사라지지 않는다.)
                element_layer.style.display = 'none';
            },
            width : '100%',
            height : '100%',
            maxSuggestItems : 5
        }).embed(element_layer);

        // iframe을 넣은 element를 보이게 한다.
        element_layer.style.display = 'block';

        // iframe을 넣은 element의 위치를 화면의 가운데로 이동시킨다.
        initLayerPosition();
    }

    // 브라우저의 크기 변경에 따라 레이어를 가운데로 이동시키고자 하실때에는
    // resize이벤트나, orientationchange이벤트를 이용하여 값이 변경될때마다 아래 함수를 실행 시켜 주시거나,
    // 직접 element_layer의 top,left값을 수정해 주시면 됩니다.
    function initLayerPosition(){
        var width = 300; //우편번호서비스가 들어갈 element의 width
        var height = 400; //우편번호서비스가 들어갈 element의 height
        var borderWidth = 5; //샘플에서 사용하는 border의 두께

        // 위에서 선언한 값들을 실제 element에 넣는다.
        element_layer.style.width = width + 'px';
        element_layer.style.height = height + 'px';
        element_layer.style.border = borderWidth + 'px solid';
        // 실행되는 순간의 화면 너비와 높이 값을 가져와서 중앙에 뜰 수 있도록 위치를 계산한다.
        element_layer.style.left = (((window.innerWidth || document.documentElement.clientWidth) - width)/2 - borderWidth) + 'px';
        element_layer.style.top = (((window.innerHeight || document.documentElement.clientHeight) - height)/2 - borderWidth) + 'px';
    }
</script>
</div>
<!-- 회원 가입 종료 -->

memberLogin.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<!-- 로그인 시작 -->
<div class="page-main">
	<h2>로그인</h2>
	<form:form action="login" id="member_login" modelAttribute="memberVO">
	<form:errors element="div" cssClass="error-color"/>
		<ul>
			<li class="floating-label">
				<form:input path="id" placeholder="아이디" autocomplete="off" cssClass="form-input"/>
				<form:label path="id">아이디</form:label>
				<form:errors element="div" path="id" cssClass="error-color"/>
			</li>
			<li class="floating-label">
				<form:password path="passwd" placeholder="비밀번호" cssClass="form-input"/>
				<form:label path="passwd">비밀번호</form:label>
				<form:errors element="div" path="passwd" cssClass="error-color"/>
			</li>
			<li>
				<label for="auto"><input type="checkbox" name="auto" id="auto">자동 로그인</label>
			</li>
		</ul>
		<div>
			<form:button class="login-btn">로그인</form:button>
			
		</div>   
	</form:form>
	<p class="align-center login-btns">
		<input type="button" value="홈으로" class="default-btn"
			  onclick="location.href='${pageContext.request.contextPath}/main/main'">
		<input type="button" value="비밀번호 찾기" class="default-btn"
			  onclick="location.href='sendPassword'">
	</p>
</div>
<!-- 로그인 끝 -->

WEB-INF > views > template

layout_mypage.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="tiles" uri="http://tiles.apache.org/tags-tiles" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title><tiles:getAsString name="title"/></title>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@100..900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="${pageContext.request.contextPath}/css/common.css" type="text/css">
<tiles:insertAttribute name="css" ignore="true"/>
</head>
<body>
	<div id="main">
		<div id="main_header">
			<!-- header의 내용이 들어옴 -->
			<tiles:insertAttribute name="header"/>
		</div>
		 <div class="side-height">
		 	<!-- nav의 내용이 들어옴 -->
		 	<div id="page_nav">
		 		<tiles:insertAttribute name="nav"/> 
		 	</div>
		 	<!-- body의 내용이 들어옴 -->
		 	<div id="page_body">
		 		<tiles:insertAttribute name="body"/> 
		 	</div>
			
		 </div>
		 <div id="main_footer" class="page-clear">
		 	<!-- footer의 내용 -->
			<tiles:insertAttribute name="footer"/> 
		 </div>
	</div>
</body>
</html>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!-- My page 메뉴 시작 -->
<div class="side-bar">
	<ul>
		<li>
			<!-- 사진의 정보를 저장한 곳에서 읽어오는 역할 -->
			<img src="${pageContext.request.contextPath}/member/photoView" width="200" height="200" class="my-photo">
			<div class="camera" id="photo_btn"><img src="${pageContext.request.contextPath}/images/camera.png" width="35"></div>
		</li>
		
		<li>
		<div id="photo_choice" style="display : none;">
			<input type="file" id="upload" accept="image/gif,image/png,image/jpeg"><br>
			<input type="button" value="전송" id="photo_submit">
			<input type="button" value="취소" id="photo_reset">
		</div>
		</li>
	</ul>
	
	<ul>
		<li>
		<input type="button" class="menu-btn" value="비밀번호 변경" onclick="changePassword">
		</li>
		<li>
		<input type="button" class="menu-btn" value="회원 탈퇴" onclick="delete">
		</li>
	</ul>
</div>
<!-- My page 메뉴 끝 -->



profile
Lucky Things🍀

0개의 댓글