REST API 게시판 만들기

jeongho park·2024년 3월 17일
0
post-thumbnail

RestAPI 개념

REST(Representational State Transfer)자원을 이름으로 구분하여 해당 자원의 상태를 주고받는 모든 것을 의미합니다.

  • 준다. Client To Server = Request
  • 받는다. Server To Client = Response

대부분의 백엔드 개발자의 입사 공고는 다음과 같다.

우대 or 자격요건 : REST API 가능

REST API의 기본적인 메서드

  • Create : SQL에서 INSERT (@POST)
  • Read : SQL에서 SELECT (@GET)
  • Update : SQL에서 UPDATE (@PATCH, @PUT)
  • Delete : SQL에서 DELETE (@DELETE)

메서드의 종류와 uri만 보고도 얘가 뭘 하고 싶어하는지 의도를 알 수 있어야함 (알잘딱깔센)

Quiz 어떤걸 할려고 하는지 의도를 파악해라

  1. POST로 tp01/board
  2. GET으로 tp01/board
  3. GET으로 tp01/board/423
  4. DELETE로 tp01/board
  5. PUT으로 tp01/board

답 :
1. POST니까 INSERT문이고 board로 끝났으니 게시판 글쓰는거 같네
2. GET이니까 가져오는거고 board로 끝났으니 게시판 전체 가져오는거 같네
3. GET인데 board뒤에 숫자가 하나 더있으니 특정한 게시판 하나를 가져오는거 같네
4. DELETE인데 board로 끝났으니 게시판 전체를 날리는건가? (애매함)
5. PUT으로 board로 끝났으니 게시판 전체를 전체 고치는건가 뭐지? (애매함)

벌써 4번 5번이 애매하다. 그러니까 Restful 하지 않는 실수가 있는것이다.
그러니 4번 5번은 특정한 값만 날려야 하므로 tp01/board/423 처럼 특정한 파라미터를 받아 바꾸거나 지워야 한다.

  1. 애매하므로 DELETE tp01/board/423
  2. 애매하므로 PUT tp01/board/423

이제 구현해보자

REST한 구조는 파라미터를 받아야함 (Controller부터 만지자)

BoardRestController 클래스

@RestController
public class BoardRestController {
	// 토큰값에서 가져온거 쓰기
	private int ur_no;
	private String ur_id;
	private String ur_name;

	private void init(HttpServletResponse response) throws IOException {
		Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
		if (authentication != null && authentication.getPrincipal() instanceof MemberDetails) {
			MemberDetails userDetails = (MemberDetails) authentication.getPrincipal();
			this.ur_no = userDetails.getUr_no();
			this.ur_id = userDetails.getUsername();
			this.ur_name = userDetails.getUr_name();
		} else {
			response.sendRedirect("/member/login");
		}
	}

	@Autowired
	private BoardService boardService;
	
	@Autowired
	private BoardLogService boardLogService;
	
	// 게시판 개수 가져오기
	@GetMapping("/api/board/countBoard")
	public int getCountBoard() {
		int cnt = boardService.getBoardCount();
		return cnt;
	}

	// 데이터 하나 가져오기
	@GetMapping("/api/board/{bd_no}")
	public Map<String, Object> getBoard(@PathVariable("bd_no") int bd_no,
			HttpServletResponse response) throws IOException {
		init(response);
		Map<String, Object> mapJson = new HashMap<>();
		List<BoardDTO> list = boardService.getBoard(bd_no);
		if(!list.isEmpty()) {
			int curHit = boardService.getCurrentBoardHit(bd_no);
			BoardDTO boardDTO = new BoardDTO();
			int increasedHit = curHit + 1;
			boardDTO.setBd_hit(increasedHit);
			boardDTO.setBd_no(bd_no);
			boardService.upHit(boardDTO);
			if(boardLogService.createBoardClickLog(boardDTO, ur_id)) {
				mapJson.put("log", "success");
			}
		} else { //게시글 없을 때
			mapJson.put("result", "fail");
			mapJson.put("message", "잘못된 접근입니다.");
			mapJson.put("redirectUrl", "/board/main");
			return mapJson;
		}
		mapJson.put("result", "success");
		mapJson.put("count", list.size());
		mapJson.put("list", list);
		return mapJson;
	}

	// 게시판 작성
	@PostMapping("/api/board")
	public Map<String, Object> writeBoard(@RequestBody BoardDTO boardDTO, HttpServletResponse response)
			throws IOException {
		Map<String, Object> mapJson = new HashMap<>();
		init(response);
		boardDTO.setUr_no(ur_no);
		boardDTO.setUr_name(ur_name);
		if (boardService.upsertBoard(boardDTO)) {
			mapJson.put("result", "success");
			mapJson.put("message", "글이 등록 되었습니다.");
			mapJson.put("redirectUrl", "/board/main");
			if(boardLogService.createBoardWriteLog(boardDTO, ur_id)) {
				mapJson.put("log", "success");
			}
		} else {
			mapJson.put("result", "fail");
			mapJson.put("message", "다시 시도하세요.");
		}

		return mapJson;
	}

	// 게시판 수정
	@PutMapping("/api/board/{bd_no}")
	public Map<String, Object> modifyBoard(@PathVariable("bd_no") int bd_no, @RequestBody BoardDTO boardDTO,
			HttpServletResponse response) throws IOException {
		Map<String, Object> mapJson = new HashMap<>();
		init(response);
		boardDTO.setBd_no(bd_no);
		boardDTO.setUr_no(ur_no);
		boardDTO.setUr_name(ur_name);
		if (boardService.upsertBoard(boardDTO)) {
			mapJson.put("result", "success");
			mapJson.put("message", "글이 수정 되었습니다.");
			mapJson.put("redirectUrl", "/board/detail?bd_no=" + bd_no);
			if(boardLogService.createBoardModifyLog(boardDTO, ur_id)) {
				mapJson.put("log", "success");
			}
		} else {
			mapJson.put("result", "fail");
			mapJson.put("message", "다시 시도하세요.");
		}
		return mapJson;
	}

	@DeleteMapping("/api/board/{bd_no}")
	public Map<String, Object> deleteBoard(@PathVariable("bd_no") int bd_no, HttpServletResponse response)
			throws IOException {
		init(response);
		Map<String, Object> mapJson = new HashMap<>();
		BoardDTO boardDTO = new BoardDTO();
		boardDTO.setBd_no(bd_no);
		boardDTO.setUr_no(ur_no);

		if (boardService.deleteBoard(boardDTO)) {
			mapJson.put("result", "success");
			mapJson.put("message", "글이 삭제되었습니다.");
			mapJson.put("redirectUrl", "/board/main");
			if(boardLogService.createBoardDeleteLog(boardDTO, ur_id)) {
				mapJson.put("log", "success"); 
			}
		} else {
			mapJson.put("result", "fail");
			mapJson.put("message", "다시 시도하세요.");
		}
		return mapJson;
	}

	// 데이터 가져오기
	@GetMapping("/api/board")
	public Map<String, Object> getBoards() {
		Map<String, Object> mapJson = new HashMap<>();
		// JWT 토큰 없으면 뱉기
		List<BoardDTO> list = boardService.getAllBoards();

		mapJson.put("result", "success");
		mapJson.put("count", boardService.getBoardCount());
		mapJson.put("list", list);

		return mapJson;
	}

}

자원을 @GetMapping, @PutMapping 등으로 구분해놓음
Controller 다음 Service로 가보자

BoardService class


@Service
@Transactional
public class BoardService {

	@Autowired
	BoardRepository boardRepository;

	// 게시판 전체 개수 가져오기
	public int getBoardCount() {
		int cnt = (int) boardRepository.count();
		return cnt;
	}

	// 게시판 데이터 가져오기
	public List<BoardDTO> getAllBoards() {
		List<BoardEntity> entityList = boardRepository.findAll();
		List<BoardDTO> list = new ArrayList<>();
		for(BoardEntity boardEntity : entityList) {
			list.add(BoardConverter.EntityToDTO(boardEntity));
		}
		return list;
	}

	// 게시판 하나 가져오기
	public List<BoardDTO> getBoard(int bd_no) {
		BoardEntity boardEntity = boardRepository.findById(bd_no).orElse(null);
		BoardDTO boardDTO = BoardConverter.EntityToDTO(boardEntity);
		if(boardEntity == null) {
			return Collections.emptyList();
		}
		return Collections.singletonList(boardDTO);
	}

	public boolean upsertBoard(BoardDTO boardDTO) {
		//List<BoardEntity> list = getBoard(boardDTO.getBd_no());
		BoardEntity boardEntity = BoardConverter.DTOToEntity(boardDTO);
		if (boardEntity != null) { //upsert 둘다 진행...
			boardRepository.save(boardEntity);
			return true;
		}
		return true;
	}

	public boolean deleteBoard(BoardDTO boardDTO) {
		int bd_no = boardDTO.getBd_no();
		int ur_no = boardDTO.getUr_no();
		BoardEntity boardEntity = boardRepository.findById(bd_no).get();
		if (boardEntity.getUr_no() == ur_no) { // 있는 게시글인지 확인
			boardRepository.deleteById(bd_no);
			return true;
		}
		return false;
	}

	// 게시판 조회수 가져오기
	public int getCurrentBoardHit(int bd_no) {
		BoardEntity boardEntity = boardRepository.findById(bd_no).orElse(null);
		if(boardEntity != null) {
			int hit = boardEntity.getBd_hit();
			return hit;
		}
		return 0;
	}

	// 게시판 조회수 올리기
	public boolean upHit(BoardDTO boardDTO) {
		BoardEntity boardEntity = boardRepository.findById(boardDTO.getBd_no()).orElse(null);
		boardEntity.setBd_hit(boardDTO.getBd_hit());
		boardRepository.save(boardEntity);
		return true;
	}
}

Service 에서 Repository로 가보자

BoardRepository interface

package kr.spring.board.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import kr.spring.board.entity.BoardEntity;

@Repository
public interface BoardRepository extends JpaRepository <BoardEntity, Integer> {
	//특별한 것만 만들기
}

JPA 형태라 Config를 해서 Entity로 받는다 Entity 클래스로 가보자

TP01 클래스


@Entity
@Table(name = "TP01BOARD")
public class BoardEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int bd_no;
    
    @Column
    private int ur_no;
    
    @Column
    private String ur_name;
    
    @Column
    private String bd_name;
    
    @Column
    private String bd_info;
    
    @Column
    private String bd_type;
    
    @Column
    private int bd_auth = 4;
    
    @Column
    private int bd_hit = 0;
    
    @Column
    private Date bd_reg_date;

    public BoardEntity() {
    }

    //Getters Setters
    public int getBd_no() {
        return bd_no;
    }

    public void setBd_no(int bd_no) {
        this.bd_no = bd_no;
    }

    public int getUr_no() {
        return ur_no;
    }

    public void setUr_no(int ur_no) {
        this.ur_no = ur_no;
    }

    public String getUr_name() {
        return ur_name;
    }

    public void setUr_name(String ur_name) {
        this.ur_name = ur_name;
    }

    public String getBd_name() {
        return bd_name;
    }

    public void setBd_name(String bd_name) {
        this.bd_name = bd_name;
    }

    public String getBd_info() {
        return bd_info;
    }

    public void setBd_info(String bd_info) {
        this.bd_info = bd_info;
    }

    public String getBd_type() {
        return bd_type;
    }

    public void setBd_type(String bd_type) {
        this.bd_type = bd_type;
    }

    public int getBd_auth() {
        return bd_auth;
    }

    public void setBd_auth(int bd_auth) {
        this.bd_auth = bd_auth;
    }

    public int getBd_hit() {
        return bd_hit;
    }

    public void setBd_hit(int bd_hit) {
        this.bd_hit = bd_hit;
    }

    public Date getBd_reg_date() {
        return bd_reg_date;
    }

    public void setBd_reg_date(Date bd_reg_date) {
        this.bd_reg_date = bd_reg_date;
    }
    
    public List<Object> toList() {
    	return Arrays.asList(bd_no, ur_no, ur_name, bd_name, bd_info, bd_type, bd_auth, bd_hit, bd_reg_date);
    }
}

나는 다중 DB를 사용하기 위해서 Configuration이 있는데, 이는 복잡하기 때문에 생략 합니다.

ajax로 api를 요청하면 그 자원을 받게끔 설정 하는 것이다.

<script type="text/javascript">
$(function(){
	//ajax 시작
	$('#board_contents').empty();
	
	$.ajax({
    	type: "get",
    	url: "${pageContext.request.contextPath}/api/board",
    	success: function (response) {
    		let boardStart = '<div>총 : '+response.count+' 개</div>';
        	boardStart += '<table><tr>';
        	boardStart += '<th>번호</th>';
        	boardStart += '<th>내용</th>';
        	boardStart += '<th>작성자</th>';
        	boardStart += '<th>작성일</th>';
        	boardStart += '<th>조회수</th>';
        	boardStart += '</tr>';
        	$('#board_contents').append(boardStart);
        	
        	$(response.list).each(function (index, item) {
	    		let output = '<tr>';
				output += '<td>'+item.bd_no+'</td>';
				output += '<td><a href="${pageContext.request.contextPath}/board/detail?bd_no='+item.bd_no+'">'+item.bd_name+'</a></td>';
				output += '<td>'+item.ur_name+'</td>';
				output += '<td>'+item.bd_reg_date+'</td>';
				output += '<td>'+item.bd_hit+'</td>';
				output += '</tr>';
	        	$('#board_contents').append(output);
	    	});
            let boardEnd = '</table>';
            $('#board_contents').append(boardEnd);
            
    	},
    error: function() {
    	alert('ajax 실패');
    	}
    });
	  
});
</script>
  • 이런식으로 자바스크립트로 요청하면 값을 가져와 화면에 뿌리면 된다.
profile
BackEnd 개발자

0개의 댓글