본문 바로가기

스프링

스프링(SPRING) 게시판 2탄(뷰 + 비즈니스로직)

반응형

이전 글에서 게시판에 대한 기초설계와 DB 테이블 생성, SQL 작성을 해보았다.

이번에는 먼저 뷰를 만들어보도록 하겠다.

뷰는 간단하게 짜보도록 하는데 w3-css와 부트스트랩을 사용해서 간단하게 만들어보았다.

Visual Studio Code를 이용해서 작업했다.

변동사항이 바로 동기화 되기 때문에 이클립스보다 편하다.

- 페이지 뷰 만들기(Visual StudioCode)

로그인 뷰
뷰 작성

위처럼 필요한 페이지의 뷰를 만들어서 이클립스로 가져가기만 하면 된다. ㅎ

만들 때 jstl로 foreach 반복할 부분 등이나 상황에 따라 다른 부분들을 생각하면 작성하면 나중에 코딩을 적용할 때 편하다.

자 이제 뷰도 만들었으니 이클립스로 가서 코딩을 시작해보자!

먼저 필요한 모든 패키지,클래스,xml등을 만들자

- 필요한 패키지및파일 만들기

이제 jsp부터 흐름에 따라 코딩을 하면 된다.

먼저 작성해둔 SQL문을 mapper.xml에 다 추가해준다.

- SQL문 mapper.xml파일에 작성

BoardMapper.xml

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

	<sql id="criteria">
		<trim prefix="and(" suffix=") " prefixOverrides="OR">
			<foreach item='type' collection="typeArr">
				<trim prefix="OR">
					<choose>
						<when test="type== 'T'.toString()">
							post_title like '%' || #{keyword}||'%'
						</when>
						<when test="type== 'C'.toString()">
							post_content like '%' || #{keyword}||'%'
						</when>
						<when test="type== 'W'.toString()">
							post_writer like '%' || #{keyword}||'%'
						</when>
					</choose>
				</trim>
			</foreach>
		</trim>
	</sql>

	<select id="selectboardList" resultType="board">
		select 
			* 
		from 
			(select
				 rownum rno,b.* 
			from 
				board_post b 
			where 
				isshow != 'N'
		<include refid="criteria"></include>
			order by 
				post_no desc
			)
		 where
		 	rno between (#{pageNum}-1)*#{amount}+1 and (#{pageNum})*#{amount}
	</select>
	
	<select id="selectPost" resultType="board">
		select 
			* 
		from 
			board_post full join board_files on post_no = file_pno
		where 
			post_no = #{postNo}
	</select>

	<select id="countTotal" resultType="int">
		select 
			count(*) 
		from 
			board_post 
		where 
			isshow != 'N'
		<include refid="criteria"></include>
	</select>
	
	<update id="updatePostHit">
		update board_post set post_hit = post_hit+1 where post_no = #{postNo}
	</update>
	
	<insert id="insertPost">
		insert into 
			board_post 
		values(
			board_post_seq.nextval,#{postWriter},#{postTitle},#{postContent},sysdate,0,'Y',#{postCategory}
		)
	</insert>
	
	<insert id="insertFile">
		insert into
			board_files
		values(
			board_file_seq.nextval,board_post_seq.currval,#{filePath},#{fileName}
		)
	</insert>
	
	<update id="updatePost">
		update board_post set isshow = 'Y'
		<if test="postTitle != null">
			, post_title = #{postTitle}
		</if>
		<if test="postContent != null">
			, post_content = #{postContent}
		</if>
		<if test="postCategory != null">
			, post_category = #{postCategory}
		</if>
		where post_no = #{postNo}
	</update>
	
	<delete id="deletePost">
		delete from board_post where post_no = #{postNo}
	</delete>

	<select id="selectComment" resultType="comment">
		<![CDATA[
			select 
				c.*,m.* 
			from 
				(select 
					rownum rno,LEVEL lev,c.* 
				from 
					board_comment c 
				where 
					comment_pno = #{postNo} and isshow != 'N' and rownum <= (#{ctr.pageNum}*#{ctr.amount})
				START WITH 
					comment_parent IS NULL 
				CONNECT BY PRIOR 
					comment_no = comment_parent
				) c join board_member m on comment_id = user_id 
			 where rno > (#{ctr.pageNum}-1)*#{ctr.amount}
		]]>
	</select>
	
	<select id="commentCountTotal" resultType="int">
		select 
			count(*) 
		from
			board_comment 
		where 
			comment_pno = #{postNo} and isshow != 'N'
	</select>
	
	<insert id="insertComment">
		insert into 
			board_comment 
		values(
			board_comment_seq.nextval,#{commentPno},#{commentId},#{commentContent},sysdate,0,
			<if test="commentParent != 0">
				#{commentParent}
			</if>
			<if test="commentParent == 0">
				null
			</if>
			,'Y'
		)
	</insert>
	
	<update id="updateThumbs" statementType="CALLABLE" parameterType="thumbs">
		{ call boardCommentThumbsUp (#{thumbsMno},#{thumbsCno})
	</update>
	
	<select id="selectThumbs" resultType="int">
		select 
			count(*) 
		from 
			board_thumbs 
		where 
			thumbs_cno = #{thumbsCno} and thumbs_mno = #{thumbsMno}
	</select>
	
</mapper>

위처럼 SQL문을 만들어주고 mapper 인터페이스에도 추가해주자.

- mapper 인터페이스에 추가

public interface BoardMapper {
	public List<BoardVO> selectboardList(Criteria ctr);

	public BoardVO selectPost(int postNo);

	public int countTotal(Criteria ctr);

	public int updatePostHit(int postNo);

	public int insertPost(BoardVO board);
	
	public int insertFile(@Param("filePath")String filePath,@Param("fileName")String fileName);

	public int updatePost(BoardVO board);

	public int deletePost(int postNo);

	public List<CommentVO> selectComment(@Param("ctr") Criteria ctr, @Param("postNo") int postNo);

	public int commentCountTotal(int postNo);

	public int insertComment(CommentVO comment);

	public void updateThumbs(ThumbsVO thumbs);

	public int selectThumbs(ThumbsVO thumbs);
}

그리고 이제 작업 순서에 맞춰서

필요한 VO, DTO 생성, service 비즈니스 로직 작성, controller 작성, jsp 데이터 처리 등등 자유롭게 하면 된다. ㅎㅎ

예시로 회원가입처리를 한번 해보도록 하겠다.

먼저 jsp로 가서 form에 내용들을 파라미터에 맞게  name을 붙여주고

- join.jsp 유효성검사 및 처리

join.jsp 의 일부

<div class="joinContainer">
	<div class="joinBox">
		<div class="joinInputBox">
			<form id="joinForm" action="/bo/join/memberjoin"accept-charset="utf-8" method="POST" class="w3-container w3-card-4 w3-light-grey w3-text-black w3-margin">
				<h2 class="w3-center">회원가입</h2>
				<div class="w3-row w3-section">
					<div class="w3-col joinIconDiv">
						<i class="w3-xxlarge fa fa-user"></i>
					</div>
					<div class="w3-rest">
						<input class="w3-input w3-border" id="userId" name="userId" type="text" placeholder="아이디">
					</div>
				</div>

				<div class="w3-row w3-section">
					<div class="w3-col joinIconDiv">
						<i class="fas fa-key fa-3x"></i>
					</div>
					<div class="w3-rest">
						<input class="w3-input w3-border" id="userPass" name="userPass" type="password" placeholder="비밀번호">
						<div class="pswdlabel"></div>
					</div>
				</div>

				<div class="w3-row w3-section">
					<div class="w3-col joinIconDiv">
						<i class="far fa-envelope fa-3x"></i>
					</div>
					<div class="w3-rest">
						<input class="w3-input w3-border" id="userEmail" name="userEmail" type="text" placeholder="이메일">
					</div>
				</div>

				<div class="w3-row w3-section">
					<div class="w3-col joinIconDiv">
						<i class="w3-xxlarge fa fa-phone"></i>
					</div>
					<div class="w3-rest">
						<input class="w3-input w3-border" id="userPhone" name="userPhone" type="text" placeholder="휴대번호(-미포함)">
					</div>
				</div>

				<div class="w3-row w3-section">
					<div class="w3-col joinIconDiv">
						<i class="fas fa-signature fa-3x"></i>
					</div>
					<div class="w3-rest">
						<input class="w3-input w3-border" id="userNick" name="userNick" type="text" placeholder="닉네임">
					</div>
				</div>
			</form>
			<button id="joinBtn" class="w3-button w3-block w3-section w3-lw3-text-black w3-ripple w3-padding">회원가입</button>
		</div>
	</div>
</div>

회원가입 시 체인지 이벤트를 이용해서 유효성 검사를 했다.

- 회원가입 유효성검사

join.jsp의 일부
<script>
    	var id = false;
    	var pass = false;
    	var phones = false;
    	var email = false;
    	var nick = false;
    	
    	$('#userId').on('change',function(){
    		var getCheck = RegExp(/^[a-zA-Z0-9]{4,15}$/);
            if($('#userid').val() == ''){
               alert('아이디를 입력해주세요.');
               $("#userid").focus();
               id = false;
               return;
            }
            if(!getCheck.test($('#userid').val())){
               alert('아이디를 형식에 맞게 입력해주세요.');
               $("#userid").val("");
               $("#userid").focus();
               id = false;
               return;
            }
    		$.ajax({
    			type:'post',
    			url:'/bo/join/idcheck',
    			data:{'userId':$(this).val()},
    			success:function(data){
    				if(data == 0){
    					alert('사용 가능한 아이디입니다.');
    					id = true;
    				}else{
    					alert('이미 존재하는 아이디입니다.');
    					id = false;
    				}
    			}
    		});
    	});
    	
    	$('#userPhone').on('change',function(){
    		var phone = $(this).val();
    		var phonePattern = /(^02.{0}|^01.{1}|[0-9]{3})([0-9]+)([0-9]{4})/g;
        	if(phonePattern.test(phone) && phone.length==11){
        	}else{
        		alert('전화번호를 제대로 입력해주세요.');
        		phones = false;
        		return;
        	}
    		$.ajax({
    			type:'post',
    			url:'/bo/join/phonecheck',
    			data:{'userPhone':phone},
    			success:function(data){
    				if(data != 0){
    					alert('이미 가입된 전화번호입니다.');
    					phones = false;
    				}else{
    					phones = true;
    				}
    			}
    		});
    	});
    	
    	$('#userPass').on('change',function(){
    		var reg = /^(?=.*?[a-zA-Z])(?=.*?[0-9])(?=.*?[#?!@$%^&*-]).{8,}$/;
            if(!reg.test($(this).val())){
               $('.pswdlabel').html(" 비밀번호는 8자 이상이어야 하며, 숫자/대소문자/특수문자를 모두 포함해야 합니다.");
               pass = false;
               return;
            }else{
               $('.pswdlabel').html('');
               pass = true;
               return;
            }
    	});
    	
    	$('#userNick').on('click',function(){
    		var nickreg = /^[a-zA-Z가-힣0-9]{3,15}$/;
            if(!nickreg.test($('#userNick').val())){
               alert('닉네임은 3~15글자 (한글,영대소문자,숫자)만 가능합니다.');
               $('#userNick').focus();
               nick = false;
               return;
            }else{
	            nick = true;            	
            }
    	});
    	
    	$('#userEmail').on('change',function(){
    		var regExp = /^[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*@[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*.[a-zA-Z]{2,3}$/i;
    		  if ($('#userEmail').val().match(regExp) != null) {
    			  email = true;
    		  }
    		  else {
    			  alert('이메일 형식이 아닙니다.');
    			  email = false;
    			  $('#userEmail').focus();
    			  return;
    		  }
    	})
    	
    	$('#joinBtn').on('click',function(){
    		if(!id || !pass || !phones || !email || !nick){
    			alert('입력하지않은 정보가 있습니다.');
    			return;
    		}else{
    			$('#joinForm').submit();
    		}
    	});
    </script>

URL에 맞게 MemberController로 가서 처리해주면 된다.

그전에 먼저 VO와 Service를 작성해주도록 하겠다.

- MemberVO 만들기

@Data
public class MemberVO { // 회원 VO

	private String userId;

	private String userPass;

	private String userEmail;

	private String userPhone;

	private String userProfile;

	private String userNick;

	private String isshow;

	private int userNo;
}

- MemberService

@Service
public class MemberServiceImpl implements MemberService {
	
	@Setter(onMethod_=@Autowired)
	private MemberMapper mapper;

	@Override
	public int joinMember(MemberVO member) {
		member.setUserPass(new PasswordMd5().passMD5(member.getUserPass()));
		int result = 0;
		if(mapper.insertMember(member) > 0) {
			result = 1;
		}else result = -1;
		return result;
	}

	@Override
	public int idCheck(String userId) {
		return mapper.selectIdCheck(userId);
	}

	@Override
	public int phoneCheck(String phone) {
		return mapper.selectPhoneCheck(phone);
	}

	@Override
	public boolean loginCheck(MemberVO member) {
		member.setUserPass(new PasswordMd5().passMD5(member.getUserPass()));
		boolean result = false;
		if(mapper.selectLoginCheck(member) > 0) result = true;
		return result;
	}

	@Override
	public MemberVO getMemberInfo(MemberVO member) {
		return mapper.selectMember(member);
	}

}

- MemberController 처리

@Controller
public class MemberController {

	@Setter(onMethod_ = @Autowired)
	private MemberService service;

	@ResponseBody
	@PostMapping("/join/idcheck") // 회원가입아이디체크
	public int idCheck(String userId) {
		return service.idCheck(userId);
	}

	@ResponseBody
	@PostMapping("/join/phonecheck") // 회원가입전화번호체크
	public int phoneCheck(String userPhone) {
		return service.phoneCheck(userPhone);
	}

	@PostMapping("/join/memberjoin") // 회원가입
	public String memberJoin(MemberVO member, HttpSession session) {
		int result = service.joinMember(member);
		if (result == 1) {
			session.setAttribute("loginInfo", service.getMemberInfo(member));
			return "redirect:/joinok";
		} else {
			return "redirect:/common/exception";
		}
	}

	@PostMapping("/login/checklogin")
	public String loginDo(MemberVO member, HttpSession session, RedirectAttributes red) {
		if (service.loginCheck(member)) {
			session.setAttribute("loginInfo", service.getMemberInfo(member));
			return "redirect:/board";
		} else {
			HashMap<String, Object> map = new HashMap<String, Object>(); // 로그인실패시 에러메시지담고 리다이렉트
			map.put("errorId", member.getUserId());
			map.put("loginErrorMsg", "아이디 또는 비밀번호를 다시 확인하세요.");
			red.addFlashAttribute("errormap", map);
			return "redirect:/login";
		}
	}
}

이렇게 하면 회원가입에 대한 처리를 모두 끝내었다.

실행결과를 보자.

회원가입 페이지

 

각 구간에 맞춰 유효성 검사를 해서 처리해준 것을 볼 수 있다.

이제 모든 사항을 만족하고 회원가입 버튼을 눌러보자.

회원가입 성공

성공적으로 회원가입이 된 것을 알 수 있다.

회원이어야 댓글과 좋아요, 글쓰기, 글 수정, 글 삭제 등을 할 수 있게 해 놓았다.

나머지도 회원가입과 동일하게 작업하면 게시판이 완성된다.

스프링에 가장 기본이면서 항상 할때마다 달라지는 게시판 만들기

끝이 없는듯하다 자주 만들어보면 기초적인 실력이 매우 상승할 것이다.

게시판 만들기 끝~ 

나머지는 모두 만들어서 깃허브에 올려두었다.

깃허브 주소 https://github.com/skawjd03/Spring-Th-Board

 

skawjd03/Spring-Th-Board

Contribute to skawjd03/Spring-Th-Board development by creating an account on GitHub.

github.com

 

반응형