AWS Back Day 67. "Spring Boot로 구현한 REST API를 활용한 AJAX 기반 사용자 등록 페이지"

이강용·2023년 4월 9일
0

Spring Boot

목록 보기
2/20

thymeleaf 다운

MVN 링크

pom.xml 의존성 추가

<dependency>
		    <groupId>org.springframework.boot</groupId>
		    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

templates > new > other > AddUser.html 생성

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<title>Insert title here</title>
		<script src="https://code.jquery.com/jquery-latest.min.js"></script>
	</head>
	<body>
		<div>
			<input class="username">
		</div>
		<div>
			<input class="password">
		</div>
		<div>
			<input class="name">
		</div>
		<div>
			<input class="email">
		</div>
		<button>등록</button>
		<script>
		</script>
	</body>
</html>

controller > UserPageController.java(class) 생성

코드를 입력하세요

서버 실행은 main 에서 반드시 실행 할것!!!

User 등록 요구사항

post
/api/user/addition

요청 JSON UserAdditionDto -> username, password, name, email
응답 UserAdditionDto (@RestController)

dto > request > UserAdditionDto(class) 생성

package com.web.study.dto.request;

import lombok.Data;

@Data
public class UserAdditionDto {
	private String username;
	private String password;
	private String name;
	private String email;
}

controller > UserRestController(class) 생성

package com.web.study.controller;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import com.web.study.dto.DataResponseDto;
import com.web.study.dto.ResponseDto;
import com.web.study.dto.request.UserAdditionDto;


@RestController
public class UserRestController {

	
	@PostMapping("/api/user/addition")
	public ResponseEntity<? extends ResponseDto> addUser(@RequestBody UserAdditionDto userAdditionDto) {
		System.out.println("서버 전송 성공");
		return ResponseEntity.ok().body(DataResponseDto.of(userAdditionDto));
	}
}

메서드 정의: public ResponseEntity<? extends ResponseDto> addUser(@RequestBody UserAdditionDto userAdditionDto)는 UserAdditionDto 타입의 객체를 입력으로 받아 ResponseDto 타입의 ResponseEntity를 반환하는 addUser 메서드를 정의
ResponseEntity는 HTTP 응답과 관련된 정보를 포함

@PostMapping 어노테이션: 이 어노테이션은 이 메서드가 HTTP POST 요청을 처리하며, "/api/user/addition" 경로로 매핑. 클라이언트가 이 경로로 POST 요청을 보내면, addUser 메서드가 실행.

@RequestBody 어노테이션: 이 어노테이션은 메서드 인자의 UserAdditionDto 객체가 HTTP 요청 본문에서 변환된 것임을 나타냄. JSON 형태의 HTTP 요청 본문이 자동으로 UserAdditionDto 객체로 변환

ResponseEntity 반환: 요청이 처리된 후, ResponseEntity.ok().body(DataResponseDto.of(userAdditionDto))를 통해 HTTP 응답을 생성하고 반환. 여기서는 HTTP 상태 코드를 '200 OK'로 설정하고 응답 본문에 DataResponseDto 객체를 포함시키며, 이 DataResponseDto 객체는 UserAdditionDto 객체를 이용하여 생성.

결과

AddUser.html 수정

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<title>Insert title here</title>
		<script src="https://code.jquery.com/jquery-latest.min.js"></script>
	</head>
	<body>
		<div>
			<input class="username">
		</div>
		<div>
			<input class="password">
		</div>
		<div>
			<input class="name">
		</div>
		<div>
			<input class="email">
		</div>
		<button type="button" class="send-button">등록</button>
		
		<script>
			window.onload = () => {
				addSendButtonEvent();
			}
			function addSendButtonEvent(){
				const sendButton = document.querySelector(".send-button");
				sendButton.onclick = () => {
					send();
				}
			}
		
			function send() {
				const username = document.querySelector(".username").value;
				const password = document.querySelector(".password").value;
				const name = document.querySelector(".name").value;
				const email = document.querySelector(".email").value;
				
				const userObj = {
				        username,
				        password,
				        name,
				        email
				    };
					
				const request ={
						type:"post", //method
						url: "http://localhost:8080/api/user/addition",
						contentType: "application/json", //요청 데이터 타입
						data: JSON.stringify(userObj), // 요청 데이터
						dataType:"json", // 응답 데이터 타입
						// 응답 성공시에 실행될 메서드 정의
						success: (response) => {
							console.log(response);
						},
						// 응답 실패시에 실행될 메서드 정의
						error: (error) => {
							console.log(error);
						}
				}
				    $.ajax(request);
			}
			
		</script>
	</body>
</html>


그렇다면 ajax는 무엇인가?

  • AJAX ( Asynchronous JavaScript and XML )는 웹 개발에서 널리 사용되는 기술로 웹 페이지의 일부부만 업데이트할 수 있게 해주는 동시에, 사용자와 상호작용하는 동안 웹 페이지의 전체 로딩이 필요하지 않음, 이로 인해 사용자 경험이 향상되며, 웹 페이지의 응답 속도도 빨라짐
  • 웹 페이지에서 서버와 비동기적으로 데이터를 교환하기 위해 JavaScipt를 사용, 이렇게함으로써 웹 페이지가 다시 로드되지 않고도 서버로부터 데이터를 받아올 수 있으며, 이 데이터를 활용해 페이지의 특정 부분을 업데이트할 수 있음
  • 원래 AJAX는 XML을 데이터 포맷으로 사용하였지만, 현재는 JSON (JavaScipt Object Notation)이 더 널리 사용되고 있음, JSON은 가볍고, 가독성이 높으며, JavaScript와 호환성이 좋음

HTTP와 HTTPS 는 무슨 차이가 있을까?

  • HTTP와 HTTPS는 웹 상에서 데이터를 전송하는 프로토콜, 두 프로토콜의 주요 차이점은 보안과 관려이 있음
  1. HTTP(Hypertext Transfer Protocol)는 웹 상에서 정보를 주고받을 수 있는 프로토콜이며 클라이언트(웹 브라우저)와 서버 간에 요청과 응답을 주고받으면서 웹 페이지의 내용이나 리소스를 제공, HTTP는 평문(암호화되지 않은 데이터) 형태로 정보를 전송하기 때문에, 중간에서 데이터가 가로채거나 조작할 수 있는 위험이 있음
  2. HTTPS(Hypertext Transfer Protocol Secure)는 HTTP에 보안 레이어를 추가한 프로토콜로, 웹 상에서 정보를 안전하게 주고받을 수 있음, HTTPS는 SSL(Secure Sockets Layer)또는 TLS(Transport Layer Security)라는 암호화 기술을 사용하여 클라이언트와 서버 간의 통신을 암호화 함. 이로 인해 중간자 공격(Man-in-the-middle attack)이나 데이터 유출 등의 보안 위험을 줄일 수 있음

💡요약하면, HTTP는 암호화되지 않은 데이터를 전송하는 반면, HTTPS는 암호화 기술을 사용하여 데이터를 안전하게 전송. 따라서 개인정보, 결제 정보 등 민감한 데이터를 다루는 웹 사이트에서는 HTTPS를 사용하는 것이 권장됨.

REST(Representational State Transfer) API

  • REST의 원칙에 기반한 웹서비스 API로, 웹 기술 및 프로토콜을 활용하여 분산 시스템을 구축하기 위한 아키텍처 스타일로, 웹 서비스 간의 상호 작용을 단순화하고 표준화하는 데 중점을 둠

REST API의 특징

  1. Stateless : 각 요청은 서버에 저장되는 클라이언트의 상태 정보 없이 독립적으로 처리됨, 이를 통해 서버의 확장성이 향상되며, 각 요청이 필요한 모든 정보를 포함해야함
  2. Client-Server Architecture : 클라이언트와 서버는 서로 독립적으로 발전할 수 있는 구조를 가지고 있음. 이로 인해 클라이언트와 서버 간의 의존성이 줄어들고, 각각의 역할이 명확해짐
  3. Cacheable : REST API는 캐시를 사용하여 응답을 저정할 수 있음. 이를 통해 클라이언트가 필요할 때 캐시된 데이터를 재사용할 수 있으며, 서버의 부하를 줄이고 성능을 향상시킬 수 있음
  4. Layered System : REST 아키텍처는 여러 계층으로 구성될 수 있으며, 각 계층은 독립적으로 관리되고 발전할 수 있음. 이를 통해 유연한 시스템 구조를 구축할 수 있음
  • 이러한 특성 덕분에 REST API는 웹 서비스 간의 통신을 간편하고 표준화된 방식으로 제공하며, 다양한 플랫폼과 언어에서 쉽게 사용할 수 있음

User 조회

전체 구조

UserPageController

package com.web.study.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class UserPageController {
	
	@GetMapping("/page/user/addition")
	public String addUser() {
		return "AddUser";
	}
	
	@GetMapping("/page/user/search")
	public String getUser() {
		return "FindUser";
	}
	
	@GetMapping("/page/users")
	public String getUsers() {
		return "UserList";
	}
}

UserPageController 클래스는 사용자 관련 페이지를 처리하는 컨트롤러로서, 다음과 같은 세가지 엔드 포인트를 제공한다.
1. @GetMapping("/page/user/addition") : 사용자 추가 페이지에 대한 요청을 처리, 이 엔드포인트가 호출되면 AddUser라는 뷰 이름을 반환하며, 이 뷰는 사용자 추가 페이지를 표시하는데 사용
2. @GetMapping("/page/user/search") : 사용자 검색 페이지에 대한 요청을 처리, 이 엔드포인트가 호출되면 FindUser라는 뷰 이름을 반환하며, 이 뷰는 사용자 검색 페이지를 표시하는데 사용
3. @GetMapping("page/users") : 사용자 목록 페이지에 대한 요청을 처리, 이 엔드포인트가 호출되면 UserList라는 뷰 이름을 반환하며, 이 뷰는 사용자 목록 페이지를 표시하는데 사용

@Controller 어노테이션은 해당 클래스가 Spring MVC(Model-View-Controller) 컨트롤러로 사용되어야 함을 나타냄
@GetMapping 어노테이션은 특정 HTTP GET 요청을 해당 메서드에 매핑함을 나타냄. 이러한 방식으로 사용자와 관련된 웹 페이지에 대한 요청을 각 메서드가 처리하고 적절한 뷰를 반환


AJAX를 사용한 사용자 등록 페이지

AddUser.html

등록
<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<title>Insert title here</title>
		<script src="https://code.jquery.com/jquery-latest.min.js"></script>
	</head>
	<body>
		<div>
			<input class="username" placeholder="username">
		</div>
		<div>
			<input class="password" placeholder="password">
		</div>
		<div>
			<input class="name" placeholder="name">
		</div>
		<div>
			<input class="email" placeholder="email">
		</div>
		<button type="button" class="send-button">등록</button>
		
		<script>
			window.onload = () => {
				addSendButtonEvent();
			}
			function addSendButtonEvent(){
				const sendButton = document.querySelector(".send-button");
				sendButton.onclick = () => {
					send();
				}
			}
		
			function send() {
				const username = document.querySelector(".username").value;
				const password = document.querySelector(".password").value;
				const name = document.querySelector(".name").value;
				const email = document.querySelector(".email").value;
				
				const userObj = {
				        username,
				        password,
				        name,
				        email
				    };
					
				const request ={
						type:"post", //method
						url: "http://localhost:8080/api/user/addition",
						contentType: "application/json", //요청 데이터 타입
						data: JSON.stringify(userObj), // 요청 데이터
						dataType:"json", // 응답 데이터 타입
						// 응답 성공시에 실행될 메서드 정의
						success: (response) => {
							console.log(response);
						},
						// 응답 실패시에 실행될 메서드 정의
						error: (error) => {
							console.log(error);
						}
				}
				    $.ajax(request);
			}
			
		</script>
	</body>
</html>

<script src="https://code.jquery.com/jquery-latest.min.js"> jQuery 라이브러리를 불러오는 부분
1.HTML : 사용자 입력을 받기 위한 input 요소들과, 이벤트 리스너를 추가할 버튼 요소가 포함
2. JavaScript : 페이지 로드 시 버튼에 이벤트 리스너를 추가하고, 버튼 클릭 시 서버로 데이터를 전송하기 위한 AJAX요청을 구성하고 실행
3. 위 코드에서의 주요 기능은 send() 함수 내의 $.ajax(request)를 통해 이루어짐. 이를 통해 서버로 JSON 형태의 사용자 데이터를 전송하고, 서버의 응답을 처리, 이러한 방식을 AJAX를 사용한 클라이언트-서버 통신이라고 부름

UserRestController

package com.web.study.controller;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import com.web.study.dto.DataResponseDto;
import com.web.study.dto.ErrorResponseDto;
import com.web.study.dto.ResponseDto;
import com.web.study.dto.request.UserAdditionDto;


class UserStore {
	public static Map<Integer, UserAdditionDto> userMap = new HashMap<>();
}

@RestController
public class UserRestController {

	
	@PostMapping("/api/user/addition")
	public ResponseEntity<? extends ResponseDto> addUser(@RequestBody UserAdditionDto userAdditionDto) {
		System.out.println("서버 전송 성공");
		Map<Integer, UserAdditionDto> userMap = UserStore.userMap;
		int maxKey = 0;
		if(!userMap.keySet().isEmpty()) {
			maxKey = Collections.max(userMap.keySet());
		}
		userMap.put(maxKey + 1 , userAdditionDto);
		System.out.println(userMap);

		
		return ResponseEntity.ok().body(DataResponseDto.of(userAdditionDto));
	}
	
	
	@GetMapping("/api/user/{id}")
	public ResponseEntity<? extends ResponseDto> getUser(@PathVariable int id) {
	    // userMap에서 id가 해당 id를 가진 객체를 응답 
	    // 만약에 해당 id가 존재하지 않으면 ErrorResponse를 응답으로 준다.
	    // errorMessage = 존재하지 않는 id입니다.
	    Map<Integer, UserAdditionDto> userMap = UserStore.userMap;
	    UserAdditionDto UserAdditionDto = userMap.get(id);
	    
	    try {
	    	if(UserAdditionDto == null) {
		    	throw new RuntimeException("존재하지 않는 id입니다.");
		    }
	    }catch (Exception e) {
            return ResponseEntity.badRequest().body(ErrorResponseDto.of(HttpStatus.BAD_REQUEST,e));
       }
	        return ResponseEntity.ok().body(DataResponseDto.of(UserAdditionDto));    
	}
	
	
	@GetMapping("/api/users")
	public ResponseEntity<? extends ResponseDto> getUsers() {
		
		return ResponseEntity.ok().body(DataResponseDto.of(UserStore.userMap.values()));
		
	}
}
  • 해당 코드는 Java로 작성된 Spring Boot 기반의 RESTful API 컨트롤러.
    UserRestController클래스는 사용자 관련 API 엔드포인트를 처리하는 컨트롤러로서 세가지 엔드포인트를 제공
  1. @PostMapping("/api/user/addition") : 사용자 추가 API 엔드 포인트, 클라이언트로부터 전달받은 UserAdditionDto 객체를 서버 측의 UserStore 클래스의 userMap에 저장. 저장이 완료되면 저장된 객체를 DataResponseDto로 감싸서 클라이언트에게 응답
  2. @GetMapping("api/user/{id})" : 특정 사용자의 정보를 반환하는 API 엔드 포인트, 클라이언트로부터 전달받은 id값을 이용하여 UserStore클래스의 userMap에서 해당 사용자 정보를 조회, 사용자 정보가 존재하면 DataResponseDto로 감싸서 응답하고, 존재하지 않으면 ErrorResponseDto를 반환하여 에러 메시지를 전달
  3. @GetMapping("/api/users") : 모든 사용자 정보를 반환하는 API 엔드 포인트, UserStore 클래스의 userMap에 저장된 모든 사용자 정보를 가져와 DataResponseDto로 감싸서 클라이언트에게 응답
  • UserStore 클래스는 userMap이라는 정적 HashMap을 포함하며, 이를 통해 사용자 정보를 메모리에 저장하고 관리
  • ResponseEntity는 HTTP 응답을 구성하기 위한 클래스로, 이를 사용하여 클라이언트에게 응답을 전송할 때 응답 본문과 함께 상태 코드를 설정할 수 있음. 이 코드에서는 ResponseEntity.ok()ResponseEntity.badRequest() 를 사용하여 상태 코드를 설정

@RestController 어노테이션은 해당 클래스가 Spring REST 컨트롤러로 사용되어야 함을 나타냄
@PostMapping, @GetMapping 어노테이션은 각각 HTTP POST 및 GET 요청을 해당 메서드에 매핑함을 나타냄,
→ 이러한 방식으로 사용자와 관련된 API 요청을 각 메서드가 처리하고 적절한 응답을 반환

엔드포인트가 무엇인가?

  • RESTful API에서 클라이언트가 특정 리소스에 접근하거나 수행할 수 있는 작업을 요청할 수 있는 URL. 즉, 엔드포인트는 API의 각 기능에 대한 경로를 나타내며, 이를 통해 클라이언트와 서버 간 데이터를 교환 할 수 있음. 일반적으로 엔드포인트는 HTTP메서드와 함께 사용되어 CRUD(Create, Read, Update, Delete)작업을 수행

UserAdditionDto

package com.web.study.dto.request;

import lombok.Data;

@Data
public class UserAdditionDto {
	private String username;
	private String password;
	private String name;
	private String email;
}

@@@

조회 성공

조회 실패

profile
HW + SW = 1

0개의 댓글