From 4b7bc16e48796c6754970d2584cb14d77131d891 Mon Sep 17 00:00:00 2001 From: youjin Date: Tue, 24 Dec 2024 11:05:58 +0900 Subject: [PATCH 1/5] =?UTF-8?q?feat=20:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=EC=97=AC=EB=B6=80=EC=97=90=EB=94=B0=EB=A1=9C=20model=20?= =?UTF-8?q?=EC=A0=95=EB=B3=B4=20=ED=99=95=EC=9D=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../modellist/DataModelCartController.java | 49 +++++++------ .../redunm/modellist/DataModelController.java | 31 ++++++--- .../redunm/modellist/ModelCartService.java | 68 +++++++++++++++++++ 3 files changed, 116 insertions(+), 32 deletions(-) create mode 100644 src/main/java/com/example/redunm/modellist/ModelCartService.java diff --git a/src/main/java/com/example/redunm/modellist/DataModelCartController.java b/src/main/java/com/example/redunm/modellist/DataModelCartController.java index e1e206b..b441302 100644 --- a/src/main/java/com/example/redunm/modellist/DataModelCartController.java +++ b/src/main/java/com/example/redunm/modellist/DataModelCartController.java @@ -1,9 +1,10 @@ package com.example.redunm.modellist; +import com.example.redunm.modellist.ModelCartService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; -import java.util.*; +import java.util.List; @RestController @RequestMapping("/api/data-models") @@ -12,14 +13,10 @@ public class DataModelCartController { @Autowired private DataModelRepository dataModelRepository; - /** - * [임시] 사용자별 장바구니 목록을 저장할 Map - * - key: 사용자 ID (문자열) - * - value: 해당 사용자가 장바구니에 담은 DataModel 리스트 - */ - private Map> userCartMap = new HashMap<>(); + @Autowired + private ModelCartService cartService; // 새로 만든 CartService - //모델 생성,삭제,추가,수정 + // 1) DataModel 기본 CRUD @GetMapping public List getAll() { return dataModelRepository.findAll(); @@ -46,27 +43,37 @@ public void delete(@PathVariable String id) { dataModelRepository.deleteById(id); } - //장바구니 기능 + // 2) 장바구니 관련 API @PostMapping("/cart/{userId}/{modelId}") public List addToCart(@PathVariable String userId, @PathVariable String modelId) { - DataModel selectedModel = dataModelRepository.findById(modelId).orElse(null); - if (selectedModel == null) { - return Collections.emptyList(); + // DB에서 modelId로 DataModel 조회 + DataModel foundModel = dataModelRepository.findById(modelId).orElse(null); + if (foundModel == null) { + // 없는 모델이면 빈 목록 반환 + return List.of(); } - List cartList = userCartMap.getOrDefault(userId, new ArrayList<>()); - - cartList.add(selectedModel); - - userCartMap.put(userId, cartList); - - return cartList; + // CartService를 통해 장바구니에 추가 + return cartService.addToCart(userId, foundModel); } - //장바구니 목록 조회 + // (B) 특정 사용자의 장바구니 조회 @GetMapping("/cart/{userId}") public List getCart(@PathVariable String userId) { - return userCartMap.getOrDefault(userId, Collections.emptyList()); + return cartService.getCart(userId); + } + + // (C) 장바구니 전체 비우기 (옵션) + @DeleteMapping("/cart/{userId}") + public void clearCart(@PathVariable String userId) { + cartService.clearCart(userId); + } + + // (D) 장바구니에서 특정 모델 제거 (옵션) + @DeleteMapping("/cart/{userId}/{modelId}") + public List removeModelFromCart(@PathVariable String userId, + @PathVariable String modelId) { + return cartService.removeModel(userId, modelId); } } diff --git a/src/main/java/com/example/redunm/modellist/DataModelController.java b/src/main/java/com/example/redunm/modellist/DataModelController.java index 1d9b2ea..4fd02f0 100644 --- a/src/main/java/com/example/redunm/modellist/DataModelController.java +++ b/src/main/java/com/example/redunm/modellist/DataModelController.java @@ -1,23 +1,32 @@ package com.example.redunm.modellist; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Controller; -import org.springframework.ui.Model; -import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import com.example.redunm.entity.User; +import jakarta.servlet.http.HttpSession; import java.util.List; -@Controller +@RestController +@RequestMapping("/api/models") public class DataModelController { @Autowired - private DataModelRepository DataModelRepository; + private DataModelRepository dataModelRepository; - @GetMapping("/models") - public String getAllModels(Model model) { - // MongoDB에서 모든 모델 데이터를 조회 - List models = DataModelRepository.findAll(); - model.addAttribute("models", models); // 데이터를 뷰로 전달 - return "modellist"; // modellist.html 렌더링 + @GetMapping + public ResponseEntity getAllModels(HttpSession session) { + User loggedInUser = (User) session.getAttribute("loggedInUser"); + if (loggedInUser == null) { + // 로그인이 안돼있으면 401 오류 뜨게하기 + return ResponseEntity.status(HttpStatus.UNAUTHORIZED) + .body("로그인이 필요합니다."); + } + + // 로그인 된 경우 mongoDB에서 model 조회 + List models = dataModelRepository.findAll(); + return ResponseEntity.ok(models); } } diff --git a/src/main/java/com/example/redunm/modellist/ModelCartService.java b/src/main/java/com/example/redunm/modellist/ModelCartService.java new file mode 100644 index 0000000..88c10af --- /dev/null +++ b/src/main/java/com/example/redunm/modellist/ModelCartService.java @@ -0,0 +1,68 @@ +package com.example.redunm.modellist; + +import com.example.redunm.modellist.DataModel; +import org.springframework.stereotype.Service; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 사용자별 장바구니(Cart)를 + * 서버 메모리 내부의 Map에 저장해두는 임시 서비스 + */ +@Service +public class ModelCartService { + + /** + * 사용자별 장바구니 목록을 저장할 Map + * key: 사용자 ID (문자열) + * value: 해당 사용자가 장바구니에 담은 DataModel 리스트 + * + * - ConcurrentHashMap: 멀티쓰레드 환경에서 안전성 보장(간단 예시) + * - 실무에서는 Redis나 DB에 저장하는 방식을 권장 + */ + private final Map> userCartMap = new ConcurrentHashMap<>(); + + /** + * 특정 사용자(userId)의 장바구니 목록 조회 + */ + public List getCart(String userId) { + return userCartMap.getOrDefault(userId, Collections.emptyList()); + } + + /** + * 장바구니에 모델 추가 + * @return 추가 후의 장바구니 전체 목록 + */ + public List addToCart(String userId, DataModel model) { + // userId가 가진 장바구니 목록 가져오기 (없으면 새 리스트) + List cartList = userCartMap.getOrDefault(userId, new ArrayList<>()); + cartList.add(model); + + // 갱신된 목록을 다시 put + userCartMap.put(userId, cartList); + + return cartList; + } + + /** + * 장바구니 전체 삭제 (옵션) + */ + public void clearCart(String userId) { + userCartMap.remove(userId); + } + + /** + * 장바구니에서 특정 모델 삭제 (옵션) + */ + public List removeModel(String userId, String modelId) { + List cartList = userCartMap.get(userId); + if (cartList == null) { + return Collections.emptyList(); + } + + cartList.removeIf(m -> m.getId().equals(modelId)); + userCartMap.put(userId, cartList); + return cartList; + } +} From 62ba67b6a0efe205a90fb6014aa40c27b55532d0 Mon Sep 17 00:00:00 2001 From: youjin Date: Wed, 25 Dec 2024 16:16:55 +0900 Subject: [PATCH 2/5] =?UTF-8?q?fix=20:=20security=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?=EB=B0=8F=20login=20GET=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../example/redunm/config/SecurityConfig.java | 19 +++++++------------ .../example/redunm/login/LoginController.java | 7 +++++++ 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/example/redunm/config/SecurityConfig.java b/src/main/java/com/example/redunm/config/SecurityConfig.java index c05763d..5e4c4d0 100644 --- a/src/main/java/com/example/redunm/config/SecurityConfig.java +++ b/src/main/java/com/example/redunm/config/SecurityConfig.java @@ -6,6 +6,7 @@ import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.web.SecurityFilterChain; @Configuration @@ -16,30 +17,24 @@ public class SecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http - - .csrf(csrf -> csrf.disable()) + .csrf(AbstractHttpConfigurer::disable) .cors(Customizer.withDefaults()) - // (3) 권한 설정 .authorizeHttpRequests(auth -> auth - // 인증 없이 열어둘 경로를 명시합니다. .requestMatchers( - "/api/auth/signup/**", // REST API 회원가입 - "/auth/signup/**", // 혹시 /auth/signup/** 로도 쓰고 있다면 - "/models/**", + "/api/auth/signup/**", // 회원가입 + "/api/auth/login/**", // 로그인 + "/auth/signup/**", // 추가적인 회원가입 경로 "/css/**", - "/js/**" + "/js/**", + "/models/**" ).permitAll() - - // 그 외 모든 요청은 인증 필요 .anyRequest().authenticated() ) - // (4) 폼 로그인 기능 자체를 비활성화 (자동 리다이렉트X, 401 Unauthorized 응답) .formLogin(form -> form.disable()) - // (5) 로그아웃 설정 (필요 시) .logout(logout -> logout .logoutUrl("/logout") .logoutSuccessUrl("/") diff --git a/src/main/java/com/example/redunm/login/LoginController.java b/src/main/java/com/example/redunm/login/LoginController.java index f3d0be8..bcc28eb 100644 --- a/src/main/java/com/example/redunm/login/LoginController.java +++ b/src/main/java/com/example/redunm/login/LoginController.java @@ -43,6 +43,13 @@ public ResponseEntity login(@RequestBody Map requestBody, return ResponseEntity.ok("로그인 성공"); } + //GET 추가 + @GetMapping + public ResponseEntity handleGetLogin() { + return ResponseEntity + .status(HttpStatus.METHOD_NOT_ALLOWED) + .body("GET 메서드는 /api/auth/login 엔드포인트에서 지원되지 않습니다. POST 메서드를 사용하세요."); + } @PostMapping("/logout") public ResponseEntity logout(HttpSession session) { From a832c2dc096abafadf6d130960e1f230a4fb6e60 Mon Sep 17 00:00:00 2001 From: youjin Date: Wed, 25 Dec 2024 16:20:21 +0900 Subject: [PATCH 3/5] =?UTF-8?q?feat=20:=20=EC=98=88=EC=99=B8=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=20=EC=BD=94=EB=93=9C=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../exception/GlobalExceptionHandler.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 src/main/java/com/example/redunm/exception/GlobalExceptionHandler.java diff --git a/src/main/java/com/example/redunm/exception/GlobalExceptionHandler.java b/src/main/java/com/example/redunm/exception/GlobalExceptionHandler.java new file mode 100644 index 0000000..518aa1c --- /dev/null +++ b/src/main/java/com/example/redunm/exception/GlobalExceptionHandler.java @@ -0,0 +1,18 @@ +package com.example.redunm.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.HttpRequestMethodNotSupportedException; + +@ControllerAdvice +public class GlobalExceptionHandler { + + @ExceptionHandler(HttpRequestMethodNotSupportedException.class) + public ResponseEntity handleMethodNotSupported(HttpRequestMethodNotSupportedException ex) { + return ResponseEntity + .status(HttpStatus.METHOD_NOT_ALLOWED) + .body("지원되지 않는 HTTP 메서드입니다. " + ex.getMethod() + "이 엔드포인트에서 지원되지 않습니다."); + } + +} From 82dcade644aa44c037c13e898c12c19f31a42959 Mon Sep 17 00:00:00 2001 From: youjin Date: Wed, 25 Dec 2024 17:17:07 +0900 Subject: [PATCH 4/5] =?UTF-8?q?fix=20:=20login=20post=20=EC=9A=94=EC=B2=AD?= =?UTF-8?q?=20=EC=B2=98=EB=A6=AC=20=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../example/redunm/login/LoginController.java | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/example/redunm/login/LoginController.java b/src/main/java/com/example/redunm/login/LoginController.java index bcc28eb..429fef4 100644 --- a/src/main/java/com/example/redunm/login/LoginController.java +++ b/src/main/java/com/example/redunm/login/LoginController.java @@ -17,22 +17,30 @@ public class LoginController { @Autowired private UserService userService; + // POST 요청 @PostMapping public ResponseEntity login(@RequestBody Map requestBody, HttpSession session) { - String username = requestBody.get("username"); - String password = requestBody.get("password"); + String email = requestBody.get("email"); + String confirmPassword = requestBody.get("ConfirmPassword"); - var optionalUser = userService.findByUsername(username); + // 이메일 또는 비밀번호가 null인 경우 + if (email == null || confirmPassword == null) { + return ResponseEntity + .status(HttpStatus.BAD_REQUEST) + .body("이메일과 비밀번호를 모두 입력해주세요."); + } + + var optionalUser = userService.findByEmail(email); if (optionalUser.isEmpty()) { return ResponseEntity .status(HttpStatus.BAD_REQUEST) - .body("존재하지 않는 아이디입니다."); + .body("존재하지 않는 이메일입니다."); } User user = optionalUser.get(); - if (!user.getPassword().equals(password)) { + if (!user.getPassword().equals(confirmPassword)) { return ResponseEntity .status(HttpStatus.BAD_REQUEST) .body("비밀번호가 일치하지 않습니다."); @@ -43,7 +51,6 @@ public ResponseEntity login(@RequestBody Map requestBody, return ResponseEntity.ok("로그인 성공"); } - //GET 추가 @GetMapping public ResponseEntity handleGetLogin() { return ResponseEntity From 88ee598b9f51ef12256554f950715fccf2b6bdce Mon Sep 17 00:00:00 2001 From: youjin Date: Wed, 25 Dec 2024 19:25:06 +0900 Subject: [PATCH 5/5] =?UTF-8?q?feat=20:=20=EC=95=94=ED=98=B8=20=EB=B3=B5?= =?UTF-8?q?=EA=B5=AC=ED=99=94=EB=B0=8F=20dto=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../example/redunm/login/LoginController.java | 17 ++++++++----- .../example/redunm/login/LoginRequest.java | 24 +++++++++++++++++++ 2 files changed, 35 insertions(+), 6 deletions(-) create mode 100644 src/main/java/com/example/redunm/login/LoginRequest.java diff --git a/src/main/java/com/example/redunm/login/LoginController.java b/src/main/java/com/example/redunm/login/LoginController.java index 429fef4..c305875 100644 --- a/src/main/java/com/example/redunm/login/LoginController.java +++ b/src/main/java/com/example/redunm/login/LoginController.java @@ -2,10 +2,12 @@ import com.example.redunm.entity.User; import com.example.redunm.service.UserService; +import com.example.redunm.login.LoginRequest; import jakarta.servlet.http.HttpSession; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.web.bind.annotation.*; import java.util.Map; @@ -17,15 +19,16 @@ public class LoginController { @Autowired private UserService userService; + private final BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); + // POST 요청 @PostMapping - public ResponseEntity login(@RequestBody Map requestBody, + public ResponseEntity login(@RequestBody LoginRequest loginRequest, HttpSession session) { - String email = requestBody.get("email"); - String confirmPassword = requestBody.get("ConfirmPassword"); + String email = loginRequest.getEmail(); + String password = loginRequest.getPassword(); - // 이메일 또는 비밀번호가 null인 경우 - if (email == null || confirmPassword == null) { + if (email == null || password == null) { return ResponseEntity .status(HttpStatus.BAD_REQUEST) .body("이메일과 비밀번호를 모두 입력해주세요."); @@ -40,7 +43,7 @@ public ResponseEntity login(@RequestBody Map requestBody, User user = optionalUser.get(); - if (!user.getPassword().equals(confirmPassword)) { + if (!passwordEncoder.matches(password, user.getPassword())) { return ResponseEntity .status(HttpStatus.BAD_REQUEST) .body("비밀번호가 일치하지 않습니다."); @@ -51,6 +54,7 @@ public ResponseEntity login(@RequestBody Map requestBody, return ResponseEntity.ok("로그인 성공"); } + // GET 요청에 대한 처리 추가 @GetMapping public ResponseEntity handleGetLogin() { return ResponseEntity @@ -58,6 +62,7 @@ public ResponseEntity handleGetLogin() { .body("GET 메서드는 /api/auth/login 엔드포인트에서 지원되지 않습니다. POST 메서드를 사용하세요."); } + // 로그아웃 처리 (POST 방식) @PostMapping("/logout") public ResponseEntity logout(HttpSession session) { session.invalidate(); diff --git a/src/main/java/com/example/redunm/login/LoginRequest.java b/src/main/java/com/example/redunm/login/LoginRequest.java new file mode 100644 index 0000000..b20c4e4 --- /dev/null +++ b/src/main/java/com/example/redunm/login/LoginRequest.java @@ -0,0 +1,24 @@ +package com.example.redunm.login; + +public class LoginRequest { + private String email; + private String password; + + // Getters and Setters + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } +}