TODAY TOTAL
분류 전체보기 (78)
[Spring] AOP

AOP(Aspect-Oriented Programming) 관점 지향 프로그래밍라는 뜻이다.

 

기술면접에서 은근 많이 나오는 질문

:: 여러 모듈의 공통적으로 사용하는 기능을 분리하여 관리함으로써 재사용성을 높여주는 프로그래밍 기법

 

관점(Aspect) == 관심사(concern) 통용됨

 

관심사를 분리 해서  핵심로직만 집중 할 수 있게 도와준다. 그것이 AOP.

 

AOP 용어 개념 

 

  • Aspect : 애플리케이션 내 여러 군데에 흩어져 있는 코드. 여러 객체에 공통적으로 적용되는 관심 사항 
  • JoinPoint : 프로그램 실행 중일 때 발생하는 메서드 실행, 생성자 호출, 필드 값 변경 등 특별한 지점을 의미
  • Advice : 특정 JoinPoint의 Aspect에 의한 동작을 의미
  • Pointcut : JoinPoint의 정규 표현식 JoinPoint가 Pointcut에 일치 할 때마다 관련된 Advice 실행
  • Weaving : Aspect를 대상 객체에 연결시켜 관점지향 객체로 만드는 과정을 의미

 

 

AOP는 AspectJ 라이브러리의 도움을 많이 받는다.

 

:: AspectJ는 AOP의 확장 기능으로 단순함과 이용성을 강조하고 폭넓게 AOP를 쓸 수 있다.(표준)

 

 

SampleServiceImpl.java

 

@Service
public class SampleServiceImpl implements SampleService{

	@Override
	public Integer doAdd(String str1, String str2) throws Exception {
		return Integer.parseInt(str1) + Integer.parseInt(str2);
	}

}

 

:: 반드시 @service 어노테이션을 사용해 이것이 서비스인 것을 표시

:: 숫자형의 문자열을 인자 값으로 받으면 해당 값을 10진수의 Integer형으로 반환 해줍니다.

 

 

@Aspect
@Log4j
@Component
public class LogAdvice {

	//Aspect 어노테이션은 해당 객체가 Aspect를 구현한다는 것을 나타냄
	//Component는 빈으로 인식 시키기 위해
    
	@Before( "execution(*org.zerock.service.SampleService*.*(..))")
	//Before. BeforeAdvice를 구현한 메서드를 추가
	//[접근 제한자] 리턴 타입 [패키지 또는 클래스 명]메소드 명(..) <- args
	public void logBefore() {
		log.info("============================");
		//
	}
	//root-context.xml의 네임스페이스 aop,context를 추가..
	
	@Before( "execution(*org.zerock.service.SampleService*.doAdd(String,String)) && args(str1,str2)")
	//execution으로 시작하는 Pointcut 설정에 doAdd 메소드를 명시.
	//뒤쪽의 &&은 logBeforeWithParam파라미터 설정. 
	//하지만 간단하게 찾는데는 좋지만 여러종류의 메서드를 찾을 땐 좋지 않다.

	public void logBeforeWithParam(String str1,String str2) {
		log.info("str1:" + str1);
		log.info("str2:" + str2);
		//
	}
	
	@AfterThrowing(pointcut = "execution(* org.zerock.service.SampleService*.*(..))",
				throwing = "exception")
	//@AfterThrowing은 지정된 대상이 예외를 발생한 후에 동작하면서 문제를 찾을 수 있도록 도와줌.

	public void logException(Exception exception) {
		log.info("Exception...");
		log.info("exception:" + exception);
		//
	}
	
	

}

 

트랜젝션 'ACID'

 

원자성 : 어떤 트랜젝션이 A와 B로 구성된다면 A,B의 처리 결과는 동일한 결과여야 한다.

 

일관성 : 트랜젝션으로 처리된 데이터와 일반 데이터사이에는 전혀 차이가 없어야 한다.

 

격리 : 트랜젝션으로 처리되는 중간에 외부에서의 간섭 X

 

영속성 : 트랜젝션이 성공적이면 그 결과는 영속적 보관

 

트랜젝션 예로는 '계좌 이체' (나의 계좌에서 출금, 입금)

 

 

 

 

 

 

 

 

'Back-End > Spring' 카테고리의 다른 글

[Spring] 댓글 구현 2  (0) 2021.08.29
[Spring] 댓글 구현  (0) 2021.08.29
[Spring] 검색 화면 처리  (0) 2021.08.11
[Spring] 검색 처리  (0) 2021.08.11
[Spring] 페이징 처리 및 번호 출력  (0) 2021.08.06
  Comments,     Trackbacks
[Spring] 댓글 구현 2

 

 

Service, dao, vo, mapper

 

mapper

 

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


	
	<select id = "getReplyListByParent" resultType="com.study.reply.vo.ReplyVO">
	<include refid="common.BEFORE_PAGING_QRY" />
	SELECT re_no
       , re_category
       , re_parent_no
       , re_mem_id
       , (SELECT mem_name FROM member WHERE mem_id = re_mem_id ) AS re_mem_name
       , re_content
       , re_ip
       , TO_CHAR(re_reg_date,'YYY.MM.DD') AS re_reg_date
	FROM reply
	WHERE re_category = #{reCategory}
	AND re_parent_no = #{reParentNo}
	ORDER BY re_no DESC
	<include refid="common.AFTER_PAGING_QRY" />	
	</select>	
	
	<select id="getReplyCountByParent" resultType="int" >
	SELECT count(1) AS cnt
	FROM reply
	WHERE re_category = #{reCategory}
	AND re_parent_no = #{reParentNo}
	</select>

<select id ="getReply" resultType="com.study.reply.vo.ReplyVO">

SELECT re_no
       , re_category
       , re_parent_no
       , re_mem_id
       , (SELECT mem_name FROM member WHERE mem_id = re_mem_id ) AS re_mem_name
       , re_content
       , re_ip
       , TO_CHAR(re_reg_date,'YYY.MM.DD') AS re_reg_date
	FROM reply
	WHERE re_no = #{reNo}
</select>


<insert id="insertReply" >
			 INSERT INTO reply (
		    re_no
		    , re_category
		    , re_parent_no
		    , re_mem_id
		    , re_content
		    , re_ip
		) VALUES (
			seq_reply.nextval
			,#{reCategory}
			,#{reParentNo}
			,#{reMemId}
			,#{reContent}
			,#{reIp}
		)
	</insert>
	
	
	<update id="updateReply">
	
		UPDATE reply
			SET re_content = #{reContent}
				,re_mod_date = SYSDATE
		 WHERE re_no = #{reNo}
	
	</update>
	
	
	<delete id="deleteReply">
		delete from reply
		where re_no = #{reNo} 
	</delete>
	
</mapper>

 

<include refid="common.BEFORE_PAGING_QRY" /> 는 공통 페이징

 

common.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="common">
	
 	<sql id="BEFORE_PAGING_QRY">
 		SELECT *
			FROM ( SELECT rownum AS rnum
						, a.* 
							FROM ( 
 	</sql>
 	
 	<sql id="AFTER_PAGING_QRY">
 		<![CDATA[
 		  			) a  
		  	where rownum <= #{endRow}  
		  ) b  							
		  where rnum BETWEEN #{startRow} AND #{endRow}
		 ]]>  
 	</sql>
 	
	
</mapper>

 

Vo

package com.study.reply.vo;

import java.io.Serializable;

import org.apache.commons.lang3.builder.ToStringBuilder;

@SuppressWarnings("serial")
public class ReplyVO implements Serializable {

		private int reNo;       		//댓글번호
		private String reCategory;   	//분류
		private int reParentNo;      	// 부모 번호
		private String reMemId;  		//작성자ID
		private String reContent;    	//댓글 내용 
		private String reIp;  			//IP
		private String reRegDate;  		//댓글 등록일자
		private String reModDate; 		//댓글 수정일자
		private String reMemName;
        
        get,set,string... 생략...

 

SerarchVO

package com.study.reply.vo;

import com.study.common.vo.PagingVO;

public class ReplySearchVO extends PagingVO{

	private String reCategory;
	/* 분류(FREE, BOARD,PDS,..) */
	private int reParentNo;
	/* 부모 번호 */
	public String getReCategory() {
		return reCategory;
	}
	public void setReCategory(String reCategory) {
		this.reCategory = reCategory;
	}
	public int getReParentNo() {
		return reParentNo;
	}
	public void setReParentNo(int reParentNo) {
		this.reParentNo = reParentNo;
	}
}

 

Service

package com.study.reply.service;

import java.util.List;

import com.study.exception.BizAccessFailException;
import com.study.exception.BizNotFoundException;
import com.study.reply.vo.ReplySearchVO;
import com.study.reply.vo.ReplyVO;

public interface IReplyService {

	/** 댓글 목록 조회 <br>
	* <b>필수 : reCategory, reParentNo </b>
	*/
    
	public List<ReplyVO> getReplyListByParent(ReplySearchVO searchVO);
	/** 댓글등록 */
	public void registReply(ReplyVO reply) ;
    
	/** 댓글 수정 <br>
	* 댓글이 존재하지 않으면 BizNotFoundException
	* 댓글 작성자와 로그인 사용자가 다른 경우 BizAccessFailException
	 * @throws BizAccessFailException 
	*/
    
	public void modifyReply(ReplyVO reply) throws BizNotFoundException, BizAccessFailException, BizAccessFailException;
    
	/**
	* 댓글 삭제 <br>
	* 해당글의 존재하지 않으면 BizNotFoundException
	* 댓글 작성자와 로그인 사용자가 다른 경우 BizAccessFailException
	*/
    
	public void removeReply(ReplyVO reply) throws BizNotFoundException, BizAccessFailException;
}

 

ServiceImpl

 

package com.study.reply.service;

@Service
public class ReplyServiceImpl implements IReplyService{
	
	@Inject
	private IReplyDao replyDao;
	
	@Override
	public List<ReplyVO> getReplyListByParent(ReplySearchVO searchVO) {
	int rowCount = replyDao.getReplyCountByParent(searchVO);
	searchVO.setTotalRowCount(rowCount);
	searchVO.setting();
	return replyDao.getReplyListByParent(searchVO);
	}
	
	@Override
	public void registReply(ReplyVO reply) {
	replyDao.insertReply(reply);
	}
	
	@Override
	public void modifyReply(ReplyVO reply) throws BizNotFoundException, BizAccessFailException {
	ReplyVO vo = replyDao.getReply(reply.getReNo());
	if(vo == null) {
	throw new BizNotFoundException("글번호[" + reply.getReNo() + "]을 조회하지 못했습니다." );
	}
	if(!vo.getReMemId().equals(reply.getReMemId())) {
	throw new BizAccessFailException("해당 글의 작성자가 아닙니다." );
	}
	replyDao.updateReply(reply);
}
	@Override
	public void removeReply(ReplyVO reply) throws BizNotFoundException, BizAccessFailException {
		ReplyVO vo = replyDao.getReply(reply.getReNo());
		if(vo == null) {
		throw new BizNotFoundException("글번호[" + reply.getReNo() + "]을 조회하지 못했습니다." );
		}
		if(!vo.getReMemId().equals(reply.getReMemId())) {
		throw new BizAccessFailException("해당 글의 작성자가 아닙니다." );
		}
		replyDao.deleteReply(reply);
	}
}

 

:: DB에서 dao로 그리고 service단으로 올 때 값 조회가 되지 않을 시 발생

 

 

Dao

package com.study.reply.dao;

@Mapper
public interface IReplyDao {

	public int getReplyCountByParent(ReplySearchVO searchVO);
	public List<ReplyVO> getReplyListByParent(ReplySearchVO searchVO);
	public ReplyVO getReply(int reNo);
	public int insertReply(ReplyVO reply);
	public int updateReply(ReplyVO reply);
	public int deleteReply(ReplyVO reply);
}

 

'Back-End > Spring' 카테고리의 다른 글

[Spring] AOP  (0) 2021.09.03
[Spring] 댓글 구현  (0) 2021.08.29
[Spring] 검색 화면 처리  (0) 2021.08.11
[Spring] 검색 처리  (0) 2021.08.11
[Spring] 페이징 처리 및 번호 출력  (0) 2021.08.06
  Comments,     Trackbacks
[Spring] 댓글 구현

댓글은 View.jsp 게시판 상세보기를 클릭 시 게시판 상세보기가 나오고 그 밑에

댓글이 나오는건데 댓글은 JavaScript에 있는 fn_reply_list() 함수를 호출하고 댓글정보, 댓글 개수를 DB에서 가져온다.

 

 

그 뒤론 더보기, 삭제, 수정, 저장 함수를 호출하는 구조

 

ajax는 많이 쓰니 이해하기!

 

 

Controller

@RestController //@Controller + @ResponsBody
public class ReplyController {
	
	@Inject
	private IReplyService replyService;
	
		@RequestMapping(value="/reply/replyList", produces = "application/json;charset=UTF-8" )
		public Map<String, Object> replyList(ReplySearchVO searchVO) throws Exception {
		List<ReplyVO> list = replyService.getReplyListByParent(searchVO);
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("result", true);
		map.put("data", list);
		map.put("count", list.size());
		return map;
		}
		// @PostMapping ("/reply/replyRegist")
		@RequestMapping(value="/reply/replyRegist", method = RequestMethod.POST)
		public Map<String, Object> replyRegist(ReplyVO reply, HttpServletRequest req, HttpSession session )
		throws Exception {
		reply.setReIp(req.getRemoteAddr());
		UserVO user = (UserVO)session.getAttribute("USER_INFO");
		reply.setReMemId(user.getUserId());
		replyService.registReply(reply);
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("result", true);
		map.put("msg", "정상 등록 되었습니다.");
		return map;
		}

 

@RestController : @Controller와 @ResponseBody가 합친 어노테이션. 쓰면 좋은 점은 메소드에 매번 @ResponseBody를 붙여 주지 않아도 된다.

 

@RequestBody : 요청한 Body 내용을 자바 오브젝트로 변환시켜주는 역할이다. 즉, POST방식의 HTTP 바디 안에 JSON으로 넘어오는 값을 VO에 바인딩한다. 요청하는 곳으로 바로 감. ( Get 요청일 경우 RequestBody를 사용 못함)

 

@ResponseBody : VO 객체를 JSON으로 바꿔서 HTTP body에 담는 스프링 @

메소드의 리턴 값을 Response의 body에 담는 역할

 

 

		reply.setReIp(req.getRemoteAddr());
		UserVO user = (UserVO)session.getAttribute("USER_INFO");
		reply.setReMemId(user.getUserId());

 

:: 로그인 한 후 댓글을 쓸 수 있고, 댓글 쓴 사람은 자기 댓글만 수정,삭제 할 수 있게 하기 위해 User 정보를 가져온다.

 

 

@RequestMapping(value="/reply/replyModify")
		public Map<String, Object> replyModify(ReplyVO reply, HttpSession session ) throws Exception {
		UserVO user = (UserVO)session.getAttribute("USER_INFO");
		reply.setReMemId(user.getUserId());
		Map<String, Object> map = new HashMap<String, Object>();
		try {
		replyService.modifyReply(reply);
		map.put("result", true);
		map.put("msg", "수정 되었습니다.");
		return map;
		} catch (BizNotFoundException e) {
		map.put("result", false);
		map.put("msg", "글이 존재하지 않습니다.");
		return map;
		} catch (BizAccessFailException e) {
		map.put("result", false);
		map.put("msg", "접근에 실패했습니다.");
		return map;
		}
	}
		
		@RequestMapping(value="/reply/replyDelete")
		public Map<String, Object> replyDelete(ReplyVO reply, HttpSession session ) throws Exception {
			UserVO user = (UserVO)session.getAttribute("USER_INFO");
			reply.setReMemId(user.getUserId());
			Map<String, Object> map = new HashMap<String, Object>();
			try {
			replyService.removeReply(reply);
			map.put("result", true);
			map.put("msg", "삭제 되었습니다.");
			return map;
			
			} catch (BizNotFoundException e) {
			map.put("result", false);
			map.put("msg", "글이 존재하지 않습니다.");
			return map;
			
			} catch (BizAccessFailException e) {
			map.put("result", false);
			map.put("msg", "접근에 실패했습니다.");
			return map;
			}
		}
}

::Key값을 string으로 value 값을 Object형으로 put 메소드를 통해 입력 할 수 있다.

 

 

 

view.jsp

 

<!-- START : 댓글 관련 영역 -->
	<div class="container">
		<!-- START : 댓글 입력 영역 -->
		<div class="panel panel-default">
			<div class="panel-body form-horizontal">
				<form name="frm_reply" action="<c:url value='/reply/replyRegist' />"
					method="post" onclick="return false;">
					<input type="hidden" name="reParentNo" value="${board.boNo}">
					<input type="hidden" name="reCategory" value="FREE">
					<div class="form-group">
						<label class="col-sm-2 control-label">댓글</label>
						<div class="col-sm-8">
							<textarea rows="2" name="reContent" class="form-control" ></textarea>
						</div>
						<div class="col-sm-2">
							<button id="btn_reply_regist" type="button" class="btn btn-sm btn-info">등록</button>
						</div>
					</div>
				</form>
			</div>
		</div>
		<!-- END : 댓글 입력 영역 -->
<!-- START : 댓글 목록 영역-->
		<div id="id_reply_list_area"></div>
		<!-- END : 댓글 목록 영역-->

		<!-- START : 더보기 버튼 영역-->
		<div class="row text-center" id="id_reply_list_more">
			<button id="btn_reply_list_more">
				<span class="glyphicon glyphicon-chevron-down" aria-hidden="true"></span>
				더보기
			</button>
		</div>
		<!-- END : 더보기 버튼 영역-->

 

<!-- START : 댓글 수정용 Modal-->
		<div class="modal fade" id="id_reply_edit_modal" role="dialog">
			<div class="modal-dialog">
				<!-- Modal content-->
				<div class="modal-content">
					<form name="frm_reply_edit"
						action="<c:url value='/reply/replyModify' />" method="post"
						onclick="return false;">
						<div class="modal-header">
							<button type="button" class="close" data-dismiss="modal">×</button>
							<h4 class="modal-title">댓글수정</h4>
						</div>
						
						<div class="modal-body">
							<input type="text" name="reNo" value="">
							<textarea rows="3" name="reContent" class="form-control"></textarea>
						</div>
						<div class="modal-footer">
							<button id="btn_reply_modify" type="button"
								class="btn btn-sm btn-info">저장</button>
							<button type="button" class="btn btn-default btn-sm"
								data-dismiss="modal">닫기</button>
						</div>
					</form>
				</div>
			</div>
		</div>
		<!-- END : 댓글 수정용 Modal-->
	</div> <!-- END : 댓글 container-->

 

:: 더보기, 수정, 삭제, 등록만 해도 이정도다;;; ㄷㄷ

 

 

그리고 자바스크립트 부분 (ajax 이해하기)

 

<!-- START : 댓글 목록 영역-->
<div id="id_reply_list_area"></div>
<!-- END : 댓글 목록 영역-->

 

 

<script type="text/javascript">
		
		<!-- START : 댓글 처리 스크립트 -->
        <!--페이지마다 댓글 보여줘야한다 -->
		var curPage = 1;
		var rowSizePerPage = 10 ;
		var parentNo = ${board.boNo};
		var sendParam = {"curPage" :  curPage
							, "rowSizePerPage" : rowSizePerPage
							, "reCategory" : "FREE"
							,  "reParentNo": ${board.boNo} };
        <!--댓글목록영역 -->
		var $reply_list_area = $('#id_reply_list_area');

 

// 댓글목록을 구하는 함수
		function fn_reply_list(){
			$.ajax({
				url : "<c:url value = '/reply/replyList' />",
				// 요청 페이지 URL정보
				dataType : 'json', // 서버로부터 전달받을 데이터 유형 (html, xml, json, script)
				data : sendParam, // 서버에 전송할 파라미터 정보
				success : function(data) {
					console.log('data.result', data.result);
					console.log('data.count', data.count);
					console.log('data.data', data.data);
					
					$.each(data.data,function(i,el){
						console.log(i,el);
						var str = '<div class="row" >'; 
							str += '<div class="col-sm-2 text-right" >'+el.reMemName+'</div>'; 
							str += '<div class="col-sm-6"><pre>'+el.reContent+'</pre></div>';
						   str += '<div class="col-sm-2" >'+el.reRegDate+'</div>';
						   str += '<div class="col-sm-2">';
						   //로그인 사용자 + 댓글 등록자
						   if('${sessionScope.USER_INFO.userId}' == el.reMemId){
						   str  += '<button name="btn_reply_edit" type="button" 
                           		class=" btn btn-sm btn-info" data-re-no="'+el.reNo+'">수정</button>';
						   str +='<button name="btn_reply_delete" type="button" 
                           		class="btn btn-sm btn-danger" data-re-no="'+el.reNo+'">삭제</button>';
						   }
						   str +='</div>';
			    			str +='</div>';
			    			$('#id_reply_list_area').append(str); //append
					}); //.each
					if(data.count < sendParam.rowSizePerPage){
					$('#id_reply_list_more').hide();
					}
				}, // 요청에 성공한 경우 호출되는 함수 (data, status, xhr )
				error : function(xhr,status,error) {
					console.log('xhr',xhr);
					console.log('status',status);
					console.log('error',error);
				}
				// 요청에 실패한 경우 호출되는 함수 (xhr, status, error)
				}); // ajax
			
		} // fn_reply_list

데이터 유형은 json , 서버에 전송할 파라미터 정보는 sendParam

성공 할 시 function(data) 함수를 실행하고

 

$.each는 JQuery를 사용해 배열을 관리하고자 할 때 each() 메서드를 사용 할 수 있다.

매개 변수로 받은 것을 사용해 for in 반복문과 같이 배열이나 객체의 요소를 검사 할 수 있는 메서드.

 

 

$.each(data.data,function(i,el)

 

컨트롤러단을 보면 data는 list다. list와 익명 i,el

 

회원이름 + 글 내용 + 작성일

 

 

	if('${sessionScope.USER_INFO.userId}' == el.reMemId){
		str  += '<button name="btn_reply_edit" 
        	type="button" class=" btn btn-sm btn-info" data-re-no="'+el.reNo+'">수정</button>';
		str +='<button name="btn_reply_delete" 
        	type="button" class="btn btn-sm btn-danger" data-re-no="'+el.reNo+'">삭제</button>';
	}

if 문은 접속한 아이디와 일치시 수정,삭제가 나타나게 함. (자기 댓글만 수정,삭제 가능)

 

수정

$(document).ready(function() {
			
		// 수정버튼 클릭
		$('#id_reply_list_area').on('click','button[name=btn_reply_edit]',function(e){
			/* $('#id_reply_edit_modal').modal('show') */
			$btn = $(this);
			$('form[name=frm_reply_edit] ').find('input[name=reNo]').val($btn.data('re-no') );
			$('form[name=frm_reply_edit] ').find('textarea[name=reContent]').val($btn.closest('div.row').find('pre').text() );
			$('#id_reply_edit_modal').modal('show');
		}); // btn_reply_edit.click
		
	
		// 모달창의 (수정)저장버튼 btn_reply_modify 클릭
		$("#btn_reply_modify").click(function(e) {
			var params = $('form[name =frm_reply_edit]').serialize();
			 // #btn_reply_regist.click
			 
			$.ajax({
				type :"POST", // 전송 방식 설정 (Defaut : GET)
				url : "<c:url value = '/reply/replyModify' />",
				dataType : 'json', // 서버로부터 전달받을 데이터 유형 (html, xml, json, script)
				data : params, // 서버에 전송할 파라미터 정보
				success : function(data) {
					if(data.result) {
						// 성공
						reNo = $('form[name=frm_reply_edit]').find('input[name=reNo]').val();
						reContent = $('form[name=frm_reply_edit] ').find('textarea[name=reContent]').val();
						$('button[data-re-no=' + reNo + ']').closest('div.row').find('pre').text(reContent);
					}else{
						alert(data.msg);
					}
					
					$('#id_reply_edit_modal').modal('hide');
					
				}, // 요청에 성공한 경우 호출되는 함수 (data, status, xhr )
				error : function(xhr,status,error) {
					console.log('xhr',xhr);
					console.log('status',status);
					console.log('error',error);
				}
				// 요청에 실패한 경우 호출되는 함수 (xhr, status, error)
				}); // ajax
			
			}); //("#btn_reply_regist").click

url : "<c:url value = '/reply/replyModify' />",

 

<c:url : URL에 자동으로 Context Path를 붙여줍니다. 편리한게 Context를 변경을 하더라도 URL을 수정 할 필요 X

 

if(data.result) {
		// 성공
		reNo = $('form[name=frm_reply_edit]').find('input[name=reNo]').val();
        
        //reply_edit 중 input name이 reNo인 것을 찾아 값 넣기
		reContent = $('form[name=frm_reply_edit] ').find('textarea[name=reContent]').val();
        //reply_edit 중 input name이 reContent인 것을 찾아 값 넣기
        
		$('button[data-re-no=' + reNo + ']').closest('div.row').find('pre').text(reContent);
			}else{
				alert(data.msg);
			}

.closest('div.row') : 선택한 요소를 포함하면서 가장 가까운 상위 요소를 선택

 

삭제

 

// 삭제버튼 클릭
		$('#id_reply_list_area').on('click','button[name=btn_reply_delete]',function(e){
			$btn = $(this);
			var params = {"reNo" : $btn.data('re-no')};
			res = confirm("정말로 삭제하시겠습니까?");
			if(res) {
				 
				 $.ajax({
					 type : "POST",
					 url : "<c:url value = '/reply/replyDelete' />",
					 dataType : "json",
					 data : params,
					 success : function(data) {
						 if(data.result){
							 reContent = $('form[name=frm_reply_edit] ').find('textarea[name=reContent]').val();
							 $('button[data-re-no=' + del + ']').closest('div.row').find('pre').text(reContent);
						 }
						 $("")
				 }
				 });
			}
			
		}); // btn_reply_delete.click

 

더보기

 

// 더보기 버튼 클릭
		$('#btn_reply_list_more').click(function(e) {
		sendParam.curPage =sendParam.curPage + 1;
		fn_reply_list();
		}); // #btn_reply_list_more.click

 

등록

 

// 등록버튼 클릭(아작스)
		$("#btn_reply_regist").click(function(e) {
			var params = $('form[name =frm_reply]').serialize();
		 // #btn_reply_regist.click
		$.ajax({
			type :"POST", // 전송 방식 설정 (Defaut : GET)
			url : "<c:url value = '/reply/replyRegist' />",
			// 요청 페이지 URL정보
			dataType : 'json', // 서버로부터 전달받을 데이터 유형 (html, xml, json, script)
			data : params, // 서버에 전송할 파라미터 정보
			success : function(data) {
				alert(data);
			}, // 요청에 성공한 경우 호출되는 함수 (data, status, xhr )
			error : function() {
				
			}
			// 요청에 실패한 경우 호출되는 함수 (xhr, status, error)
			}); // ajax
		
		}); //("#btn_reply_regist").click
		
		// 4. 초기화 함수 호출
		fn_reply_list();
		}); // ready
		
		</script>

'Back-End > Spring' 카테고리의 다른 글

[Spring] AOP  (0) 2021.09.03
[Spring] 댓글 구현 2  (0) 2021.08.29
[Spring] 검색 화면 처리  (0) 2021.08.11
[Spring] 검색 처리  (0) 2021.08.11
[Spring] 페이징 처리 및 번호 출력  (0) 2021.08.06
  Comments,     Trackbacks
[Oracle] 숫자 함수

 

ceil(n) : n과 같거나 가장 큰 정수를 반환

floor(n) : ceil 함수의 반대. n보다 작거나 가장 큰 정수를 반환

 

(ceil) 1행_ 21706.6 -> 가장 큰 정수니깐 21707

(ceil) 6행_ 32680 -> 같으니깐 32680같은 값이 반환

 

(floor) 1행_ 21706.6 -> ceil의 반대로 작은 정수 반환 21706

 

 

round(n,i) :  n을 소수점 기준 (i+1)번 째에서 반올림한 결과를 반환한다.

trunc(n1,n2) : 반올림하지 않고 n1을 소수점 기준으로 n2자리에서 무조건 잘라낸 결과를 반환 

(음수이면  소수점 기준 왼쪽 자리에서 잘라낸다.)

 

(round) 디폴트 값은 0, 소수점 첫 번째 자리에서 반올림이 일어난다.

(round) round(10.154,1)이면 10.2가 나오는데, 소수점기준으로 첫번째를 반올림하기 때문이다.) 

(round) 그럼 i값이 음수이면? 소수점 기준으로 왼쪽을 반환한다. 사진을보면 21706.6의 소수점 왼쪽 첫번째 6을 반올림

21710이 된다.

 

(trunc) trunc는 소수점 기준이라는건 round와 동일하지만 다른건 반올림을 하지 않고, n1을 소수점 기준으로 n2자리에서 무조건

반환한다.

 

 

power(n1,n2) : n1을 n2 제곱한 결과를 반환

(n1이 음수이면 n2는 무조건 정수여야 한다.)

 

 mod(n1,n2) : n1을 n2으로 나눈 나머지 값을 반환

 

 

  Comments,     Trackbacks
[Tomcat] 톰캣 45초 서버 오류 해결하기

 

 

 

 

내 노트북이 워낙 느려서 그런가 톰캣 45초 오류가 발생했다.

 

:: Server Tomcat v8.5 Server at localhost was unable to start within 45 seconds. If the server requires more time, try increasing the timeout in the server editor.

 

 

서버 더블클릭

 

 

 

Start (in seconds)를 설정된 값보다 30~50 올리면 해결이 된다.

  Comments,     Trackbacks
[Spring] 검색 화면 처리

 

검색창을 게시판에 보여지게 하고 그 검색을 계속 끌고 가야한다.(페이지넘길때도 검색을 끌고 가기)

 

 

 

list.jsp

 <!-- 검색  -->
   <form id='searchForm' action="/board/list" method="get">
   <select name = "type">
     <option value=""${pageMaker.cri.type == null?"selected":""}>---</option>
     <option value="T"${pageMaker.cri.type eq 'T'?"selected":""}>제목</option>
     <option value="C"${pageMaker.cri.type eq 'C'?"selected":""}>내용</option>
     <option value="W"${pageMaker.cri.type eq 'W'?"selected":""}>작성자</option>
     <option value="TC"${pageMaker.cri.type == 'TC'?"selected":""}>제목+내용</option>
     <option value="TCW"${pageMaker.cri.type == 'TCW'?"selected":""}>제목+내용+작성자</option>
   </select>
     <input type = 'text' name = 'keyword' value='${pageMaker.cri.keyword }'>
     <input type = 'hidden' name = 'pageNum' value='${pageMaker.cri.pageNum }'>
     <input type = 'hidden' name = 'amount' value='${pageMaker.cri.amount }'>
     <button class='btn btn-default'>Search</button>
   </form>

 

BoardMapper.xml

<sql id="criteria">
  <trim prefix="(" suffix=") AND">
	<foreach collection="typeArr" item="type" separator="OR">
		<if test="type == 'T'.toString()">
			title like '%'||#{keyword}||'%'
		</if>
		
		<if test="type == 'C'.toString()">
			content like '%'||#{keyword}||'%'
		</if>
		
		<if test="type == 'W'.toString()">
			writer like '%'||#{keyword}||'%'
		</if>
	</foreach>
	</trim>
  </sql>

option - selected 속성은 페이지가 로드될 때 옵션 중에서 미리 선택되어지는 옵션을 말함

 

pageNum을 hidden으로 안보이게 하고 자바스크립트 함수를 만들어 페이지번호를 찾게 한다.

 

 

#searchForm 자바스크립트

var searchForm = $("#searchForm");
  	
  	$("#searchForm button").on("click",function(e){
  		e.preventDefault();
  		console.log(".....................");
  		
  		searchForm.find("input[name='pageNum']").val(1);
  		
  		searchForm.submit();
  	});

 

근데 여기서 페이지를 넘길때도 검색 값을 들고 가야한다.

 

그럴려면 actionForm에 type hidden을 줘서 keyword와 type을 써서 유지되게 한다.

 

<form id='actionForm' action="/board/list" method='get'>
  <input type='hidden' name='pageNum' value = '${pageMaker.cri.pageNum}'>
  <input type='hidden' name='amount' value = '${pageMaker.cri.amount}'>
  <input type='hidden' name='type' value = '${pageMaker.cri.type}'>
  <input type='hidden' name='keyword' value = '${pageMaker.cri.keyword}'>
</form>

 

 

get.jsp에도 똑같이 hidden type,keyword를 추가해야한다.

 

 

'Back-End > Spring' 카테고리의 다른 글

[Spring] 댓글 구현 2  (0) 2021.08.29
[Spring] 댓글 구현  (0) 2021.08.29
[Spring] 검색 처리  (0) 2021.08.11
[Spring] 페이징 처리 및 번호 출력  (0) 2021.08.06
[Spring] 인덱스,힌트와 ROWNM  (0) 2021.08.05
  Comments,     Trackbacks
[Spring] 검색 처리

 

검색 할때는 특정 키워드를 입력 시 게시판에 있는 그  키워드에 맞게 나와야 한다.

 

그전에 먼저 알아야 할 동적 태그들이 있다 (MyBatis)

 

 

if

<if test="key == 'T'.toString()">
			title like #{val}
		</if>

test에 조건을 쓰고 그 조건이 true일 때.. 출력 (key가 T면 title lie #{val})

 

trim

<trim prefix="(" suffix=") AND">
	<foreach collection="typeArr" item="type" separator="OR">
		<if test="type == 'T'.toString()">
			title like '%'||#{keyword}||'%'
		</if>

trim은 단독으로 절대 사용안한다. if나 forEach,choose랑 같이 쓰인다.

 

prefix :: 쿼리 가장 앞에 붙여준다.

 

그러면 suffix는? -> 가장 뒤에 붙여준다.

 

trim 밑에 foreach는 리스트나 맵 배열 등 루프를 돌 때 사용

collection :: 전달받은 인자, 리스트나 배열 형태만 가능

item :: 배열은 키가 필요하지 않고 item에 별칭 즉 루프돌때 쓸 변수명 쓰기

separator :: 반복 되는 사이에 출력할 문자열 ( 루프돌 때마다 OR 붙이기)

 


 

<sql id="criteria">
  <trim prefix="(" suffix=") AND">
	<foreach collection="typeArr" item="type" separator="OR">
		<if test="type == 'T'.toString()">
			title like '%'||#{keyword}||'%'
		</if>
		
		<if test="type == 'C'.toString()">
			content like '%'||#{keyword}||'%'
		</if>
		
		<if test="type == 'W'.toString()">
			writer like '%'||#{keyword}||'%'
		</if>
	</foreach>
	</trim>
  </sql>

그렇게 해서 BoardMapper.xml에 SQL 검색 구문은 다음과 같은데

 

여기서 제일 위에 있는 sql태그 좀 생소한데

 

조각이라고 하는거 같다.

 

sql 태그 안에 검색 구문을 넣고 

 

기존 검색 구문 자리에

 

<include refid="criteria"></include>

써주면 조각이 불러온다.

이러면 코드가 간결해지고 중복되는 쿼리는 좀 더 편해진다.

 

 


 

 

태그 정리를 하다보니 먼저 해야하는데 깜빡했다.

 

Criteria.java

 

	private String type; 

	private String keyword;

여기서 중요한게 String타입이다 하지만 forEach로 돌릴려면 배열이나 리스트,맵,셋 으로 해야 하는데...

어떻게 해야할까?

 

public String[] getTypeArr() {
		return type == null? new String[] {}: type.split("");

String[] 타입에 getTypeArr() 추가하면 해결 된다. (검색조건은 한글자)

 

split :: 문자열 자르기

 

 

'Back-End > Spring' 카테고리의 다른 글

[Spring] 댓글 구현  (0) 2021.08.29
[Spring] 검색 화면 처리  (0) 2021.08.11
[Spring] 페이징 처리 및 번호 출력  (0) 2021.08.06
[Spring] 인덱스,힌트와 ROWNM  (0) 2021.08.05
[Spring] 조회 처리 및 페이지 이동  (0) 2021.08.04
  Comments,     Trackbacks
[Oracle]SQL Developer 11C -> 18C 변경 후 오류

컴퓨터가 갑자기 블루스크린이 뜨는 바람에 다 날라가 버렸다.

 

다시 프로그램을 깔고 있는데....

 

이제 오라클에서 11c를 지원을 하지 않는다고 한다. 하... 진짜 오라클은 6개월마다 맨날 홈페이지 UI도 바꾸고

 

진짜 새로운거 엄청 좋아한다 뭐 새롭게 바뀌지도 않는거 같던데

 

여튼 11c를 쓰다가 18c로 쓰면 별 다른 점은 없지만 DB접속 새로 만들기 할때 문제가 생긴다.

 

 

 

상태: 실패 -테스트 실패: IO 

오류: The Network Adapter could not establish the connection (CONNECTION_ID=ODUWUjr8RmaLywJY2kQw9w==)

 

 

11c에서는 로컬호스트라고 써도 저장이 가능했는데 이젠 저 호스트이름에 IP를 써야한다.

 

cmd를 열어 윈도우는 ipconfig, 리눅스는 ifconfig 를 치고 자기의 아이피를  파란색 1번 호스트이름에 입력하면 된다.

 

 

다른 방법으로는

 

C:\app\윤고랭이\product\18.0.0\dbhomeXE\network\admin

에 listener.ora에 들어가도 나온다.

 

 

  Comments,     Trackbacks