2020년 12월 28일은 2020 53으로 나오기를 바라는데

계속 2020 01로 나오길래 찾아봤습니다.

 

Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("Asia/Seoul"));
cal.setFirstDayOfWeek(Calendar.THURSDAY);

--> 첫째 주의 기준을 목요일로 설정하면

DateFormatUtils.format(cal, "yyyyww")

--> 202053

Transactional은 Proxy기반으로 동작하기 때문에 직접호출(자기 호출)은 Transaction이 적용되지 않습니다.

 

즉, txTest2메소드에서 txTest, txTest1을 직접 호출하는 방식으로는 두 메소드의 Transaction이 먹지 않습니다.

 

처음에 하셨던것처럼 Controller를 통해 txTest, txTest1을 호출하시거나

 

txTest, txTest1 메소드를 외부 클래스로 빼서(인터페이스 기반이어야합니다) txTest2에서 호출하는 방식으로 

 

Transaction을 적용하시기 바랍니다.

출처: https://redgura.tistory.com/422 [우썅]

 

 

결론 : 내부 메소드 호출시 Transaction 적용 안됨

'Web Programming > SPRINGBOOT' 카테고리의 다른 글

Http 통신과 Socket 통신  (0) 2021.02.16
P2P  (0) 2021.02.10
@value 어노테이션 사용시 기본값  (0) 2021.01.25
스케줄 기능 사용하기  (0) 2020.11.30
@Transactional  (0) 2020.06.11

서버에서 스케줄로 등록하여 배치 실행을 할 수 있는 @Scheduled annotation

스케줄 설정을 변수화

 

고정 지연시간

@Scheduled(fixedDelayString = "${fixedDelay.in.milliseconds}") 

 

일정 간격

@Scheduled(fixedRateString = "${fixedRate.in.milliseconds}") 


cron expression 이용

@Scheduled(cron = "${cron.expression}")

'Web Programming > SPRINGBOOT' 카테고리의 다른 글

Http 통신과 Socket 통신  (0) 2021.02.16
P2P  (0) 2021.02.10
@value 어노테이션 사용시 기본값  (0) 2021.01.25
@Transaction 사용 시 내부 메소드 간의 호출  (0) 2020.12.31
@Transactional  (0) 2020.06.11

예를 들어 아래와 같은 URL일 경우

http://127.0.0.1:8080/contextpath/servlcetpath/index.jsp?seq=1&name=test

 

httpServletRequest.getRequestURL()

http://127.0.0.1:8080/contextpath/servlcetpath/index.jsp

 

httpServletRequest.getRequestURI()

/contextpath/servlcetpath/index.jsp?


httpServletRequest.getContextPath()

/contextpath


httpServletRequest.getServletPath()

/servlcetpath/index.jsp

 

httpServletRequest.getQueryString()

seq=1&name=test

 

httpServletRequest.getServerName()

127.0.0.1


httpServletRequest.getServerPort()

8080

 

트랜잭션이란

트랜잭션(Transaction 이하 트랜잭션)이란, 데이터베이스의 상태를 변화시키기 위해서 수행하는 작업의 단위이다.

쪼개질 수 없는 하나의 작업 단위를 의미한다.

 

Rollback : 작업 도중 문제가 발생할 때, 트랜잭션 실행 전 상태로 되돌리는 것

                 이전 Commit을 한 곳까지 복구된다.

Commit : 하나의 트랙잭션 과정이 종료된 후 변경된 내용을 모두 저장한다. 트랙잰션 작업 내용을 실제 DB에 저장

 

ACID(Atomicity, Consistency, Isolation, Durability) 성질이 보장된다.

원자성(Atomicity) 한 트랜잭션 내에서 실행한 작업들을 하나의 단위로 처리한다. 작업이 모두 DB에 저장되거나 모두 반영되지 않거나
일관성(Consistency) 트랜잭션 작업 처리 결과가 항상 일관성이 있어야한다. 
독립성(Isolation) 트랜잭션 수행 시 다른 트랜잭션의 작업이 끼어들지 못하도록 보장한다.
동시에 수행되는 트랜잭션들이 서로 영향을 미치지 못한다.
영속성(Durability) 성공적으로 수행된 트랜잭션은 영원히 반영되어야 한다. 성공적으로 처리된 결과가 항상 저장된다.

 

 

스프링부트에서 지원하는 선언적 트랜잭션

설정파일이나 어노테이션을 이용하여 트랜잭션의 범위와 롤백 규칙 등을 정의할 수 있다.

선언 방식

  1) <tx:advice> 태그를 이용한 트랜잭션 처리

  2) @Transactional 어노테이션을 이용한 트랜잭션 설정

 

하나의 메소드에 여러 로직을 한 단위로 처리할 때, 하나의 로직에 문제가 발생하면 롤백을 시켜줘야 한다!!

 

AOP(Aspect Oriented Programming) : 관점 지향 프로그래밍

핵심 기능과 공통 기능을 분리시켜 핵심 기능에서 필요시 공통 기능 사용하는 방식.

 

트랜잭션은 AOP를 통해 이루어지므로 즉 다이나믹 프록시를 이용해 AOP가 동작된다는 말이다.

==> 인터페이스를 통해 호출이 되어야 트랜잭션 기능이 동작한다.

 

'Web Programming > SPRINGBOOT' 카테고리의 다른 글

Http 통신과 Socket 통신  (0) 2021.02.16
P2P  (0) 2021.02.10
@value 어노테이션 사용시 기본값  (0) 2021.01.25
@Transaction 사용 시 내부 메소드 간의 호출  (0) 2020.12.31
스케줄 기능 사용하기  (0) 2020.11.30

1. JAVA

썬 마이크로 시스템즈(Sun Microsystems, Inc. 이하 썬)에서 개발한 객체지향 프로그래밍 언어이다.

1991년 썬에서 제임스 고슬링이 이끄는 팀에 의해 개발된 C와 C++에 기반을 둔 '오크(Oak)'라는 언어에서 시작되었다. 오크는 가정용 단말기에 적용하려는 목적으로 만들어졌으나, 인터넷이 발전하면서 인터넷 환경에 적합한 언어로써 개발 방향을 바꾸며 1995년 자바(JAVA)라는 이름으로 새롭게 소개되었다.

 

** 객체란 데이터, 행위, 아이덴티티를 가지고 있는 것

데이터 : 객체의 상태 정보 저장

행위 : 객체가 어떻게 해야하는지

아이덴티티 : 어떠한 객체를 다른 객체와 구분하는 것

 

** 클래스란 객체를 생성하는 템플릿

객체를 생성하기 위해 메모리에 올릴 바이너리 형태 프로그램 코드

 

 

2. JAVA에서 사용하는 용어

2-1) JVM

JVM은 Java Virtual Machine(자바 가상 머신)의 약자이다.

자바언어로 작성된 프로그램은 바이트코드(Bytecode)라는 특수한 바이너리 형태로 변환된다.

여기서 바이트코드란 하드웨어에 종속적이지 않으며 가상 머신을 통해 해석되고 실행되는 이진 표현법이다.

자바 바이트코드를 실행하기 위해서는 JVM이라는 특수한 가상 머신이 필요하며, 이 가상 머신은 자바 바이트코드를 스마트폰, 컴퓨터, Max, 리눅스 등 어느 플랫폼에서나 동일한 형태로 실행시킨다.

 

2-2) JRE

JRE는 Java Runtime Environment(자바 실행환경)의 약자이다.

JVM이 자바 프로그램을 동작시킬 때 필요한 구성요소로 라이브러리 파일들과 기타 파일들을 가지고 있다. 

 

2-3) JDK

JDK는 Java Development Kit(자바 개발도구)의 약자이다.

JRE와 자바 프로그램 개발에 필요한 구성요소로 javac.exe(자바 컴파일러, 자바 소스코드를 바이트코드로 컴파일), java.exe(자바 인터프리터, 컴파일러가 생성한 바이트코드를 해석하고 실행)등의 파일을 포함한다.

 

2-4) JAVA SE

JAVA SE는 Java platform Standard Edition의 약자로 자바 API의 집합체이다.

 

2-5) JAVA EE

JAVA EE는 Java platform Enterprise Edition의 약자로 자바를 이용한 서버 측 개발을 위한 플랫폼이다.

 

2-6) JAVA ME

JAVA ME는 Java platform Micro Edition의 약자로 임베디드 장비를 위한 것으로 JAVA SE의 기능을 축소한 것이다.

 

 

3. JAVA 특징

  • 객체지향 언어 : 객체 지향 프로그래밍(Object Oriented Programing, OOP). 클래스 계층 구조, 상속성, 다형성, 그리고 캡슐화를 지원한다.
  • 플랫폼 독립적 : 자바 프로그램은 JVM을 통해 어느 운영체제나 하드웨어에서도 동일하게 실행된다.
  • 멀티 스레드 : 운영체제의 도움 없이 JVM에서 하나의 프로그램에 다수의 스레드가 동시에 실행할 수 있는 환경을 지원한다.
  • 오픈소스 : 오픈소스 언어로, 자바 프로그램에서 사용하는 오픈소스 라이브러리가 많다. 이로 인해 생산성을 증가하고 개발 시간을 단축하며, 유지보수 비용을 절감할 수 있다.
  • 속도가 느리다 : 한 번 컴파일로 JVM에 의해 기계어로 번역되고 실행하는 과정을 거치기 때문에 완전한 기계어보다는 속도가 느리다.
  • 보안 취약 : 컴파일된 바이트코드는 실행코드가 포함되어, 디코딩하면 소스를 그대로 보이는 단점이 있다. 

 

** 상속은 자식 클래스가 부모 클래스로부터 물려받는 것

다중 상속은 불가능하며 단일 상속만 허용된다. 하지만 부모 클래스는 여러 자식 클래스에게 상속이 가능하다.

예시 - class 자식 클래스 extends 부모 클래스 { ... }

 

** 캡슐화란 객체의 속성(data)과 동작(method)을 하나로 묶는 것

캡슐화된 클래스의 변수들은 클래스 외부에서 접근 불가능하며 오직 캡슐화된 클래스 내부의 메소드만을 통해 변수 조작이 가능하다. 

예시 - private 접근 제어자를 사용해 변수를 선언하며 setter과 getter 메소드를 통해 변수 조작

 

** 다형성이란 같은 자료형에 여러 객체를 대입하여 다양한 결과를 얻어내는 성질

다형성의 개념을 보여주는 방법은 두 가지가 있다. Overriding과 Overloadinf

Overriding : 부모 클래스의 상속을 받은 자식클래스에서 부모클래스의 메소드 내의 로직을 새롭게 정의한다. 부모 클래스를 상속받는 여러 자식 클래스들이 같은 이름의 다른 기능을 하는 메소드를 정의하고 사용할 수 있다. (같은 이름의 메소드지만 구현하는 클래스마다 다른 기능을 한다)

Overloading : 하나의 클래스에서 같은 이름의 메소드를 여러개 가질 수 있다. (단 메소드 인자의 타입이나 개수 등은 달라야 한다)

 

4. JAVA 컴파일 과정

  • 자바 컴파일러는 javac 명령어를 통해 자바 소스 코드(.java 파일)로부터 바이트코드(.class 파일)를 생성한다.
  • 바이트코드는 JVM에 로드되고 JVM에서 실행 엔진에 의해 기계어로 해석되어 메모상에 배치된다.
  • 실행 엔진에는 interpreter방식과 JIT(Just In Time)방식이 있으며, Interpreter에 의해 바이트코드를 한 줄씩 읽어 실행하다가 적절한 시점에 바이트코드 전체를 컴파일하고 해당 코드를 직접 실행한다.

 

 

틀린 부분 있으면 댓글로 말씀해주세요. 수정하겠습니다!

1. CRUD URI

method uri 설명
GET /board/list/{page} 게시판 목록 조회
POST /board/write 게시판 글 등록
GET /board/detail/{id} 게시판 상세 보기
POST /board/detail/file 파일 다운로드
POST /board/update/{id} 게시판 글 수정
DELETE /board/delete/{id} 게시판 글 삭제
DELETE /board/delete/file/{save_file_name} 파일 삭제
POST /board/write/reply/{id} 댓글 등록
DELETE /board/delete/reply/{reply_id} 댓글 삭제

 

 

2. Controller 작성

restful하게 작성하였습니다.

 

2-1) 게시판 목록 조회

	/** 게시판 목록 조회 */
	@RequestMapping(value="/list/{page}", method=RequestMethod.GET)
	@ResponseBody
	public Map<String, Object> getBoardList(@PathVariable int page, HttpServletRequest request, HttpServletResponse response, PagingVO pagingVO) throws Exception {
		Util util = new Util();

		Paging pageMaker = new Paging();
		pagingVO.setPage(page);

		pageMaker.setPagingVO(pagingVO);
		pageMaker.setTotalCount(boardService.getBoardCount());
		
		List<Map<String,Object>> board = boardService.getBoardList(pagingVO);
		
		Map<String, Object> data = new HashMap<String, Object>();
		data.put("board", board);
		data.put("total_count", pageMaker.getTotalCount());
		data.put("prev", pageMaker.isPrev());
		data.put("next", pageMaker.isNext());
		data.put("start_page", pageMaker.getStartPage());
		data.put("end_page", pageMaker.getEndPage());
		data.put("page", pagingVO.getPage());
		
		Map<String, Object> output = new HashMap<String, Object>();
		
		output = util.successTrue("getBoardList success!", data);
	
		return output;
	}

16번 라인 : 게시판 목록에 보여줄 데이터들(게시글 제목, 작성자, 작성일, 조회수)

17번 라인 : 총 게시물 수

18번-22번 라인 : 페이징 처리를 위한 번호

 

결과보기

페이지당 게시물 수를 10개로 했기 때문에 board 리스트에 데이터가 10개가 들어가 있습니다.

더보기
{
    "data": {
        "next": true,
        "end_page": 5,
        "start_page": 1,
        "total_count": 60,
        "prev": false,
        "page": 1,
        "board": [
            {
                "reg_date": "2020-01-15 12:38:01",
                "del_flag": "N",
                "hit": 10,
                "mod_date": "2020-01-15 17:59:30",
                "id": 60,
                "writer": "writer_60",
                "title": "title_60",
                "content": "content_60"
            },
            {
                "reg_date": "2020-01-15 12:38:01",
                "del_flag": "N",
                "hit": 0,
                "mod_date": "2020-01-15 12:38:01",
                "id": 59,
                "writer": "writer_59",
                "title": "title_59",
                "content": "content_59"
            },
            {
                "reg_date": "2020-01-15 12:38:01",
                "del_flag": "N",
                "hit": 0,
                "mod_date": "2020-01-15 12:38:01",
                "id": 58,
                "writer": "writer_58",
                "title": "title_58",
                "content": "content_58"
            },
            {
                "reg_date": "2020-01-15 12:38:01",
                "del_flag": "N",
                "hit": 0,
                "mod_date": "2020-01-15 12:38:01",
                "id": 57,
                "writer": "writer_57",
                "title": "title_57",
                "content": "content_57"
            },
            {
                "reg_date": "2020-01-15 12:38:01",
                "del_flag": "N",
                "hit": 0,
                "mod_date": "2020-01-15 12:38:01",
                "id": 56,
                "writer": "writer_56",
                "title": "title_56",
                "content": "content_56"
            },
            {
                "reg_date": "2020-01-15 12:38:01",
                "del_flag": "N",
                "hit": 0,
                "mod_date": "2020-01-15 12:38:01",
                "id": 55,
                "writer": "writer_55",
                "title": "title_55",
                "content": "content_55"
            },
            {
                "reg_date": "2020-01-15 12:38:01",
                "del_flag": "N",
                "hit": 0,
                "mod_date": "2020-01-15 12:38:01",
                "id": 54,
                "writer": "writer_54",
                "title": "title_54",
                "content": "content_54"
            },
            {
                "reg_date": "2020-01-15 12:38:01",
                "del_flag": "N",
                "hit": 0,
                "mod_date": "2020-01-15 12:38:01",
                "id": 53,
                "writer": "writer_53",
                "title": "title_53",
                "content": "content_53"
            },
            {
                "reg_date": "2020-01-15 12:38:01",
                "del_flag": "N",
                "hit": 0,
                "mod_date": "2020-01-15 12:38:01",
                "id": 52,
                "writer": "writer_52",
                "title": "title_52",
                "content": "content_52"
            },
            {
                "reg_date": "2020-01-15 12:38:01",
                "del_flag": "N",
                "hit": 0,
                "mod_date": "2020-01-15 12:38:01",
                "id": 51,
                "writer": "writer_51",
                "title": "title_51",
                "content": "content_51"
            }
        ]
    },
    "success": true,
    "message": "getBoardList success!",
    "error": null
}

 

2-2) 게시판 글 등록

	/** 게시판 등록 */
	@RequestMapping(value="/write", method=RequestMethod.POST)
	@ResponseBody
	public void insertBoard(MultipartHttpServletRequest request
			, @RequestParam String title
			, @RequestParam String writer
			, @RequestParam String content) throws Exception {
		
		BoardVO boardVO = new BoardVO();
		
		boardVO.setTitle(title);
		boardVO.setWriter(writer);
		boardVO.setContent(content);
		
		boardService.insertBoard(boardVO);
		
		Upload upload = new Upload();
		upload.uploadFile(boardService, request);
	}

게시글의 제목, 작성자, 내용, 그리고 첨부파일을 받아 게시판 글을 등록합니다.

(파일 첨부는 따로 포스팅하겠습니다.)

 

2-3) 게시판 글 상세보기

	/** 게시판 상세 */
	@RequestMapping(value="/detail/{id}", method=RequestMethod.GET)
	@ResponseBody
	public Map<String, Object> boardDetail(@PathVariable("id") int id, ReplyVO replyVO) throws Exception {
		Util util = new Util();
		
		BoardVO boardVO = new BoardVO();
		boardVO.setId(id);
		
	    Map<String, Object> detail = boardService.getBoardDetail(boardVO);
	    List<Map<String,Object>> file = boardService.getFileList(boardVO);
	    List<Map<String,Object>> reply = replyService.getReplyList(boardVO);

	    Map<String, Object> data = new HashMap<String, Object>();
	    
	    data.put("detail", detail);
	    data.put("file", file);
		data.put("reply", reply);
		
		Map<String, Object> output = new HashMap<String, Object>();
		
		output = util.successTrue("getBoardList success!", data);
	    
	    return output;
	}

결과보기

더보기
{
    "data": {
        "file": [
            {
                "save_file_name": "1579060044553_게시판테이블.txt",
                "reg_date": 1579060044000,
                "del_flag": "N",
                "extension": ".txt",
                "board_id": 60,
                "mod_date": 1579060044000,
                "id": 1,
                "org_file_name": "게시판테이블.txt",
                "file_size": 1465
            }
        ],
        "detail": {
            "reg_date": "2020-01-15 12:38:01",
            "del_flag": "N",
            "hit": 12,
            "mod_date": "2020-01-20 15:28:42",
            "id": 60,
            "writer": "writer_60",
            "title": "title_60",
            "content": "content_60"
        },
        "reply": [
            {
                "reg_date": "2020-01-15 12:47:49",
                "reply_id": 1,
                "reply_writer": "writer_01",
                "board_id": 60,
                "reply_content": "content_01"
            },
            {
                "reg_date": "2020-01-15 12:47:59",
                "reply_id": 2,
                "reply_writer": "writer_02",
                "board_id": 60,
                "reply_content": "content_02"
            },
            {
                "reg_date": "2020-01-15 12:48:50",
                "reply_id": 3,
                "reply_writer": "writer_03",
                "board_id": 60,
                "reply_content": "content_03"
            }
        ]
    },
    "success": true,
    "message": "getBoardList success!",
    "error": null
}

 

2-4) 게시판 글 수정

	/** 게시판 수정 */
	@RequestMapping(value="/update/{id}", method=RequestMethod.POST)
	@ResponseBody
	public void boardUpdatePost(@PathVariable("id") int id
			, @RequestParam String title
			, @RequestParam String writer
			, @RequestParam String content
			, MultipartHttpServletRequest request) throws Exception {
		
		BoardVO boardVO = new BoardVO();
		
		boardVO.setId(id);
		boardVO.setTitle(title);
		boardVO.setWriter(writer);
		boardVO.setContent(content);
		
		boardService.updateBoard(boardVO);
		
		Upload upload = new Upload();
		upload.uploadFile(boardService, request);
	}

처음에 수정 메소드를 PUT으로 작성하였지만 @RequestParam 과 PUT메소드에 문제가 생겨 POST메소드로 변경했습니다.

참고자료 -> 

 

2-5) 게시판 글 삭제

	/** 게시판 삭제 */
	@RequestMapping(value="/delete/{id}", method=RequestMethod.DELETE)
	@ResponseBody
	public Map<String, Object> boardDelete(@PathVariable int id) throws Exception {
		Util util = new Util();
		
        BoardVO boardVO = new BoardVO();
        boardVO.setId(id);
		boardService.deleteBoard(boardVO);
		
		Map<String, Object> output = new HashMap<String, Object>();
		
		output = util.successTrue("boardDelete success!", null);
		return output;
	}

 

 

3. Service 작성

DB 연동 이외의 부분 처리

	@Inject
	BoardDAO boardDAO;
	
	@Override
	public void insertBoard(BoardVO boardVO) throws Exception {
	    boardDAO.insertBoard(boardVO);
	}

	@Override
	public List<Map<String, Object>> getBoardList(PagingVO pagingVO) throws Exception {
		return boardDAO.getBoardList(pagingVO);
	}

	@Override
	public Map<String, Object> getBoardDetail(BoardVO boardVO) throws Exception {
		boardDAO.updateHit(boardVO);
		return boardDAO.getBoardDetail(boardVO);
		
	}

	@Override
	public void updateBoard(BoardVO boardVO) throws Exception {
		boardDAO.updateBoard(boardVO);
	}

	@Override
	public void deleteBoard(BoardVO boardVO) throws Exception {
		boardDAO.deleteBoard(boardVO);
	}
	
	@Override
	public int getBoardCount() throws Exception {
		return boardDAO.getBoardCount();
	}
	
	@Override
	public int getBoardId() throws Exception {
		return boardDAO.getBoardId();
	}

 

 

4. DAO 작성

	@Inject
	private SqlSession sqlSession;
	
	private static String namespace = "com.seongim.mvc_board.mapper.BoardMapper";

	@Override
	public void insertBoard(BoardVO boardVO) throws Exception {
		sqlSession.insert(namespace + ".insertBoard", boardVO);
	}

	@Override
	public List<Map<String, Object>> getBoardList(PagingVO pagingVO) throws Exception {
		return sqlSession.selectList(namespace + ".getBoardList", pagingVO);
	}

	@Override
	public Map<String, Object> getBoardDetail(BoardVO boardVO) throws Exception {
		return sqlSession.selectOne(namespace + ".getBoardDetail", boardVO);
	}

	@Override
	public void updateBoard(BoardVO boardVO) throws Exception {
		sqlSession.update(namespace + ".updateBoard", boardVO);
	}

	@Override
	public void deleteBoard(BoardVO boardVO) throws Exception {
		sqlSession.update(namespace + ".deleteBoard", boardVO);
	}
	
	@Override
	public void updateHit(BoardVO boardVO) throws Exception {
		sqlSession.update(namespace + ".updateHit", boardVO);
	}
	
	@Override
	public int getBoardCount() throws Exception {
		return sqlSession.selectOne(namespace + ".getBoardCount");
	}
	
	@Override
	public int getBoardId() throws Exception {
		return sqlSession.selectOne(namespace + ".getBoardId");
	}

 

 

5. Mapper 작성

<mapper namespace="com.seongim.mvc_board.mapper.BoardMapper">

<insert id="insertBoard" parameterType="BoardVO">
	INSERT INTO BOARD (
		title,
		content,
		writer
	) VALUES (
		#{title},
		#{content},
		#{writer}
	)
</insert>

<select id="getBoardList" resultType="hashmap">
	 	SELECT
	 		id,
	 		title,
	 		content,
	 		writer,
	 		hit,
	 		CAST(DATE_FORMAT(reg_date, '%Y-%m-%d %H:%i:%s') AS CHAR(19) ) AS reg_date,
	 		CAST(DATE_FORMAT(mod_date, '%Y-%m-%d %H:%i:%s') AS CHAR(19) ) AS mod_date,
	 		del_flag
	 	FROM
	 		board
	 	WHERE
	 		del_flag = 'N'
	 	ORDER BY id DESC
	 	LIMIT #{pageStart}, #{perPageNum}
</select>

<select id="getBoardDetail" parameterType="BoardVO" resultType="hashmap">
	<![CDATA[
	 	SELECT
	 		id,
	 		title,
	 		content,
	 		writer,
	 		hit,
	 		CAST(DATE_FORMAT(reg_date, '%Y-%m-%d %H:%i:%s') AS CHAR(19) ) AS reg_date,
	 		CAST(DATE_FORMAT(mod_date, '%Y-%m-%d %H:%i:%s') AS CHAR(19) ) AS mod_date,
	 		del_flag
	 	FROM 
	 		board
	 	WHERE 
	 		del_flag = "N"
	 		AND
	 		id = #{id}
	 ]]>
</select>

<update id="updateBoard" parameterType="BoardVO">
	UPDATE 
 		BOARD
 	SET
 		title = #{title},
 		content = #{content},
 		writer = #{writer}		
 	WHERE 
 		id = #{id}
</update>

<update id="deleteBoard" parameterType="BoardVO">
	UPDATE 
 		BOARD
 	SET
 		del_flag = "Y"		
 	WHERE 
 		id = #{id}
</update>

<update id="updateHit" parameterType="BoardVO">
	UPDATE 
 		board
 	SET
 		hit = hit + 1		
 	WHERE 
 		id = #{id}
</update>

<select id="getBoardCount" resultType="Integer">
	<![CDATA[
		SELECT
			count(*)
		FROM
			BOARD
		WHERE
			del_flag = 'N'
	]]>
</select>

<select id="getBoardId" resultType="Integer">
	<![CDATA[
		SELECT
			count(*)
		FROM
			BOARD
	]]>
</select>
</mapper>

 

 

6. 페이지 View 만들기

jquery를 사용해 ajax 통신이 가능하도록 작성하였습니다.

 

6-1) 게시판 목록 페이지

GET방식으로 2-1의 게시판 목록 데이터를 json형태의 data로 받아왔습니다.

더보기
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>게시판 목록</title>

<link rel="stylesheet" type="text/css" href="/css/commom/common.css"/>

<script type="text/javascript" src="http://code.jquery.com/jquery-3.4.1.js"></script>
<script>

	$(document).ready(function() {
		getBoardList();
	});
	
	function getBoardList() {
		var page = getUrlVars().page;
		if(!page) {
			page=1;
		}
		$.ajax({
			type : "GET",
			url : "/board/list/"+page,
			dataType : "json",
			contentType : "application/json",
			success : function(data) {
				
				var str = "";
				
				var total_count = data.data.total_count;
				var page = data.data.page;
				var prev = data.data.prev;
				var next = data.data.next;
				var start_page = data.data.start_page;
				var end_page = data.data.end_page;		
				var board = data.data.board;
						
				var idx = total_count - (page - 1) * 10;
				
				$("#total_count").html(total_count);

           		if(total_count > 0) {
					for(var i = 0; i < board.length; i++) {
						var id = board[i].id;
						var title = board[i].title;
						var hit = board[i].hit;
						var writer = board[i].writer;
						var reg_date = board[i].reg_date;
						
						str += "<tr>";
						str += "<td>" + idx + "</td>";
						str += "<td><a href='boardDetail.jsp?id=" + id + "'>" + title + "</a></td>";
						str += "<td>" + hit + "</td>";
						str += "<td>" + writer + "</td>";
						str += "<td>" + reg_date + "</td>";
						str += "</tr>";
						
						idx -= 1;
					}
           		} else {
           			str += "<tr>";
    				str += "<td colspan='5'> 등록된 글이 존재하지 않습니다. </td>";
    				str += "</tr>";
           		} //if~else
           			
           		$("#tbody").html(str);
           		
           		str = "";
           		
           		str += "<ul style='display:inline;'>";
           		
           		if(prev) {
           			str += "<li style='display:inline;'>";
           			str += "<a href='boardList.jsp?page=" + (start_page - 1) + "'><i>이전</i></a>";
           			str += "</li>";
           		}
           		
           		for(var i = start_page; i <= end_page; i++) {
           			//page = i;
           			str += "<li style='display:inline;'>";
           			str += "<a href='boardList.jsp?page=" + i + "'><i>" + i + "</i></a>";
           			str += "</li>";
           			
           		}
           		
           		if(next && end_page > 0) {
           			str += "<li style='display:inline;'>";
           			str += "<a href='boardList.jsp?page=" + (end_page + 1) + "'><i>다음</i></a>";
           			str += "</li>";
           		}
           		
           		$("#div_page").html(str);
			},
			error : function(err) {
				alert("ERROR : " + err);
			}
		});
		
	}
	
	function getUrlVars() {
	    var vars = [], hash;
	    var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
	    for(var i = 0; i < hashes.length; i++) {
	        hash = hashes[i].split('=');
	        vars.push(hash[0]);
	        vars[hash[0]] = hash[1];
	    }
	    return vars;
	}
	
</script>
</head>
<body>
<div id="wrap">
	<div id="container">
		<div class="inner">
			<h1>게시판 목록</h1>
			<form id="boardForm">
				<input type="hidden" id="current_page" name="current_page" value="1" />
				
				<div>
					<span>전체 게시글 수 : <b><span id="total_count"></span></b>개</span>
				</div>
				
				<table width="100%" border="1">
					<colgroup>
						<col width="10%" />
						<col width="25%" />
						<col width="10%" />
						<col width="15%" />
						<col width="20%" />
					</colgroup>
					<thead>
						<tr>
							<th>글번호</th>
							<th>제목</th>
							<th>조회수</th>
							<th>작성자</th>
							<th>작성일</th>
						</tr>
					</thead>
					<tbody id="tbody" style="text-align:center;">
						
					</tbody>
				</table>
			</form>
			<br>
			<div style="float:right;">
				<button type="button" onclick="location.href='boardWrite.jsp'">작성</button>
			</div>
			<br>
			<div id="div_page" style="float:center; width:20%; margin:0 auto;">
			</div>
		</div>
	</div>
</div>
</body>
</html>

 

6-2) 게시판 글 작성 페이지

formData로 title, writer, content, 그리고 files를 받아오고

POST방식으로 Controller를 호출하며 formData를 전송합니다.

46번 라인 : 성공 시 게시판 목록 페이지로 이동합니다.

더보기
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>게시판 목록</title>

<link rel="stylesheet" type="text/css" href="/css/commom/common.css"/>

<script type="text/javascript" src="http://code.jquery.com/jquery-3.4.1.js"></script>
<script>

	function postBoard() {
		
		if($("#title").val()) {
			var title = $("#title").val();
		} else {
			alert("제목을 입력해주세요!");
			return;
		}
		
		if($("#writer").val()) {
			var writer = $("#writer").val();
		} else {
			alert("작성자를 입력해주세요!");
			return;
		}
		
		if($("#content").val()) {
			var content = $("#content").val();
		} else {
			alert("내용을 입력해주세요!");
			return;
		}
		
		var formData = new FormData(document.getElementById("postForm"));
		
		$.ajax({
			type : "POST",
			url : "/board/write",
			enctype : "multipart/form-data",
			processData : false,
			contentType : false,
			data : formData,
			success : function() {
				alert("게시글이 등록되었습니다.");
				location.href="boardList.jsp";
			},
			error : function(err) {
				alert("ERROR : " + err);
			}
		});
	
	}
    
</script>

</head>
<body>
<div id="wrap">
	<div id="container">
		<div class="inner">
			<h1>게시판 등록</h1>
			<form method="post" name="postForm" id="postForm" enctype="multipart/form-data">
				<input type="hidden" name="id" value="1" />
				<table width="100%" border="1">
					<colgroup>
						<col width="40%" />
						<col width="60%" />
					</colgroup>
					<tbody id="tbody" style="text-align:center;">
						<tr>
							<td>제목</td>
							<td><input type="text" name="title" id="title" placeholder="제목을 입력하세요"></td>
						</tr>
						<tr>
							<td>작성자</td>
							<td><input type="text" name="writer" id="writer" placeholder="이름을 입력하세요"></td>
						</tr>
						<tr>
							<td>내용</td>
							<td><textarea type="text" name="content" id="content" rows="10"></textarea></td>
						</tr>
						<tr>
							<td colspan="2">
								다중 파일 추가<input type="file" name="files" id="files" multiple="multiple">
							</td>
						</tr>
					</tbody>
				</table>
				<div style="float:right;">
					<input type="button" onclick="postBoard();" value="등록하기">
	        		<button type="button" onclick="location.href='boardList.jsp'">목록으로</button>
				</div>
			</form>
		</div>
	
	</div>

</div>
</body>
</html>

 

6-3) 게시판 글 상세보기 페이지

더보기
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>게시판 상세</title>

<script type="text/javascript" src="http://code.jquery.com/jquery-3.4.1.js"></script>
<script>

	$(document).ready(function() {
		getBoardDetail();
	});

	function getBoardDetail() {
		var id = getUrlVars().id;
		$.ajax({
			type : "GET",
			url : "/board/detail/" + id,
			dataType : "json",
			contentType : "application/json",
			success : function(data) {
				
				var detail = data.data.detail;
				var file = data.data.file;
				var reply = data.data.reply;
				
				$("#title").html(detail.title);
				$("#hit").html(detail.hit);
				$("#writer").html(detail.writer);
				$("#reg_date").html(detail.reg_date);
				$("#content").html(detail.content);
				
				$("#update_href").prop("href", "boardUpdate.jsp?id=" + detail.id);
				
				str = "";
				
				if(file.length > 0) {
					
					for(var i = 0; i < file.length; i++) {
						var save_file_name = file[i].save_file_name;
						var org_file_name = file[i].org_file_name;
						
						str += "<form id='downForm' method='post' action='/board/detail/file'>";
						str += "<input type='hidden' name='saveFileName' value='" + save_file_name + "'>";
						str += "<input type='hidden' name='originFileName' value='" + org_file_name + "'>";
						str += "<input type='submit' id='click' value='" + save_file_name + "'>";
						str += "<br>"
						str += "</form>";

					}
					
				} else {
					str = "<p>업로드 파일이 없습니다.</p>";
				}
				
				$("#file").html(str);
				
				str ="";
				if(reply.length > 0) {
					for(var i = 0; i < reply.length; i++) {
						var reply_id = reply[i].reply_id;
						var reply_writer = reply[i].reply_writer;
						var reply_content = reply[i].reply_content;
						var reg_date = reply[i].reg_date;
			
						str += "<tr>";
						str += "<td>" + reply_writer + "</td>";
						str += "<td>" + reply_content + "</td>";
						str += "<td>" + reg_date + "</td>";
						str += "<form id='replyDelForm'> method='post'>";
						str += "<td><input type='hidden' name='reply_id' value='" + reply_id + "'>";
						str += "<button type='button' onclick='deleteReply(" + reply_id + ")';)>X</button></td>";
						str += "</form>";
						str += "</tr>";
					} //for
				} else {
					str += "<tr><td colspan='4'>댓글이 없습니다.</td></tr>";
				} //if~else
				
				$("#reply").html(str);
				
			},
			error : function(err) {
				alert("ERROR : " + err);
			}
		});
	}
	
	function getUrlVars() {
	    var vars = [], hash;
	    var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
	    for(var i = 0; i < hashes.length; i++) {
	        hash = hashes[i].split('=');
	        vars.push(hash[0]);
	        vars[hash[0]] = hash[1];
	    }
	    return vars;
	}
	
	function deleteBoard() {
		var id = getUrlVars().id;
		$.ajax({
			type : "DELETE",
			url : "/board/delete/" + id,
			dataType : "json",
			contentType : "application/json",
			success : function(data) {
				console.log("삭제완료");
				alert("게시글이 삭제되었습니다");
				location.href="boardList.jsp";
			},
			error : function(err) {
				alert("ERROR : " + err);
			}

		});
	}
	
	function postReply() {
		var formData = new FormData(document.getElementById("replyForm"));
		console.log(formData.has('updateBoard'));
		var id = getUrlVars().id;
		
		$.ajax({
			type : "POST",
			url : "/board/write/reply/" + id,
			data : formData,
			dataType : "json",
			contentType: false,
			processData: false,
			success : function(data) {
				alert("댓글이 등록되었습니다");
				location.href="boardDetail.jsp?id=" + id;
			},
			error : function(err) {
				alert("ERROR : " + err);
			}
		});
	}
	
	//댓글 삭제
	function deleteReply(reply_id) {
		var id = getUrlVars().id;
		$.ajax({
			type : "DELETE",
			url : "/board/delete/reply/" + reply_id,
			dataType : "json",
			contentType: false,
			processData: false,
			success : function(data) {
				alert("댓글이 삭제되었습니다");
				location.href="boardDetail.jsp?id=" + id;
			},
			error : function (request, status, error){
				alert("code:" + request.status +"\n" + "message:" + request.responseText + "\n" + "error:" + error);
			}

		});
	}
	
	//파일 다운로드
	function downloadFile() {

		var formData = new FormData(document.getElementById("downForm"));
		console.log(formData.has('saveFileName'));
		var id = getUrlVars().id;
		var xhr = new XMLHttpRequest();
		
		$.ajax({
			type : "POST",
			url : "/board/detail/file",
			data : formData,
			processData: false,
            contentType: false,
			success : function() {
			},
			error : function (request, status, error){
				alert("code:" + request.status +"\n" + "message:" + request.responseText + "\n" + "error:" + error);
			}
		});
		
		
	}

</script>
</head>
<body>
<div id="wrap">
	<div id="container">
		<div class="inner">	
			<h2>게시글 상세</h2>		
				<table width="100%" border="1">
				    <colgroup>
				        <col width="15%">
				        <col width="35%">
				        <col width="15%">
				        <col width="*">
				    </colgroup>
				    <tbody id="tbody" style="text-align:center;">
				    	<tr>
				    		<td>제목</td>
							<td id="title"></td>
							<td>조회수</td>
							<td id="hit"></td>
						</tr>
						<tr>
							<td>작성자</td>
							<td id="writer"></td>
							<td>등록일</td>
							<td id="reg_date"></td>
						</tr>
						<tr>
							<td>내용</td>
							<td colspan='3' id="content" style='height:500px;'></td>
						</tr>
						<tr>
							<td colspan="2">
								<p>파일 다운로드</p>
							</td>
							<td colspan="2" id="file">
								
							</td>
						</tr>
				    </tbody>
				</table>	
	
			<br>
			<div style="float:right;">
				<button type="button" onclick="location.href='boardList.jsp'">목록</button>
				<a id="update_href" href = ""><button type="button">수정</button></a>
				<button type="button" onclick="deleteBoard();">삭제</button>
			</div>
			<br>
			<br>
			<div style="width:100%;">
				<form id="replyForm" name="replyForm" method="post" enctype="multipart/form-data">
			      	<div>작성자 : <input type="text" name="reply_writer" id="reply_writer" placehoder="작성자명"></div>
					<div>내&nbsp;&nbsp;&nbsp;용 : <textArea name ="reply_content" id="reply_content" cols="60" rows="5"></textArea></div>	
					<div>
						<input type="button" onclick="postReply();" value="등록">
						<input type="button" value="취소">
					</div>
				</form>
			</div>
		    <table width="100%" border="1">
		    	<colgroup>
					<col width="20%">
				    <col width="50%">
				    <col width="20%">
				    <col width="*">
				</colgroup>
				<thead>
					<tr>
						<th>작성자</th>
						<th>댓글내용</th>
						<th>등록일</th>
						<th>삭제</th>
					</tr>
				</thead>
		    	<tbody id="reply" style="text-align:center;">
		    	</tbody>
		    </table>
		</div>
	</div>
</div>
</body>
</html>

 

6-4) 게시판 글 수정 페이지

게시판 글 작성 페이지를 일부 변형하여 작성했습니다.

더보기
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>게시판 수정</title>

<script type="text/javascript" src="http://code.jquery.com/jquery-3.4.1.js"></script>
<script>
	$(document).ready(function() {
		getBoardDetail();
	});
	
	function getBoardDetail() {
		var id = getUrlVars().id;
		$.ajax({
		type : "GET",
		url : "/board/detail/" + id,
		dataType : "json",
		contentType : "application/json",
		success : function(data) {
			var detail = data.data.detail;
			var file = data.data.file;
			
			$("#title").val(detail.title);
			$("#update_writer").val(detail.writer);
			$("#update_content").html(detail.content);
			
			var str = "";
			if(file.length > 0) {
				for(var i = 0; i < file.length; i++) {
					var save_file_name = file[i].save_file_name;
					var extension = file[i].extension;
			
					str += "<input type='hidden' name='extension' id='extension' value='" + extension + "'>";
					str += "<input type='button' value='" + save_file_name + "' ";
					str += "onclick='deleteFile(" + "\"" + save_file_name + "\", " + "\"" + extension + "\"" + ");' name='saveFileName'>";
					str += "<br>";
					console.log("file : " + save_file_name);
					console.log("file : " + extension);
				}
			}
			
			str += "<input type='file' name='files' multiple='multiple'>";
			
			$("#file").html(str);
		},
		error : function(err) {
			alert("ERROR : " + err);
		}
		});

	}
	
	//파일 삭제
	function deleteFile(save_file_name, extension) {
		var save_file_name = save_file_name;	
		var id = getUrlVars().id;
		$.ajax({
			type : "DELETE",
			url : "/board/delete/file/" + save_file_name,
			enctype : "multipart/form-data",
			processData : false,
			contentType : false,
			success : function() {
				alert("파일이 삭제되었습니다");
				location.href="boardUpdate.jsp?id=" + id;
			},
			error : function(err) {
				alert("ERROR : " + err);
			}
		});
	}
	
	function getUrlVars() {
	    var vars = [], hash;
	    var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
	    for(var i = 0; i < hashes.length; i++) {
	        hash = hashes[i].split('=');
	        vars.push(hash[0]);
	        vars[hash[0]] = hash[1];
	    }
	    return vars;
	}
	
	//게시글 수정
	function updateBoard() {
		
		if($("#title").val()) {
			var title = $("#title").val();
		} else {
			alert("제목을 입력해주세요!");
			return;
		}
		
		if($("#update_writer").val()) {
			var writer = $("#update_writer").val();
		} else {
			alert("작성자를 입력해주세요!");
			return;
		}
		
		if($("#update_content").val()) {
			var content = $("#update_content").val();
		} else {
			alert("내용을 입력해주세요!");
			return;
		}
		
		var formData = new FormData(document.getElementById("updateForm"));
		
		//console.log(formData.has('content')); // true);
		var id = getUrlVars().id;
		
		$.ajax({	
			type : "POST",
			url : "/board/update/" + id,
			enctype : "multipart/form-data",
			processData : false,
			contentType : false,
			data : formData,
			success : function() {
				alert("게시글이 수정되었습니다.");
				location.href="boardList.jsp";
			},
			error : function (request, status, error){
				alert("code:" + request.status +"\n" + "message:" + request.responseText + "\n" + "error:" + error);
			}

		});
	
	}

	
</script>
</head>
<body>

<div id="wrap">
	<div id="container">
		<div class="inner">
			<h1>게시판 수정</h1>
			<form  method="post" name="updateForm" id="updateForm" enctype="multipart/form-data">
				<table width="100%" border="1">
					<colgroup>
						<col width="40%" />
						<col width="60%" />
					</colgroup>
					<tbody id="tbody" style="text-align:center;">
						<tr>
							<td>제목</td>
							<td><input type="text" name="title" id="title" value=""></td>
						</tr>
						<tr>
							<td>작성자</td>
							<td><input type="text" name="writer" id="update_writer" value=""></td>
						</tr>
						<tr>
							<td>내용</td>
							<td><textarea type="text" name="content" id="update_content" rows="10"></textarea></td>
						</tr>
						<tr>
							<td>
								<p>파일</p>
							</td>
							<td id="file">
								
							</td>
						</tr>		
					</tbody>
				</table>
				<div style="float:right;">
					<input type="button" onclick="updateBoard();" value="수정하기">
	        		<button type="button" onclick="location.href='boardList.jsp'">뒤로가기</button>
				</div>
			</form>
		</div>
	</div>
</div>
</body>
</html>

 

톰캣 구동 시 콘솔창에서 한글 깨짐 문제 해결법

 

1. 실행창(win + r)에서 regedit 입력 후 레지스트리 편집기를 열어줍니다.

 

 

 

2. 컴퓨터 > HKEY_CURRNET_USER > Console 위치에 새로운 키값(Tomcat)을 생성해줍니다.

 

 

3. 해당 키값(Tomcat) 안에 DWORD(32비트) 값을 추가해준 후 이름을 CodePage로 수정해줍니다.

 

 

4. CodePage의 값을 다음과 같이 수정해줍니다.

 

 

5. 톰캣 서버 재시작 후 한글깨짐 현상 해결 완료,

 

 

 

+ Recent posts