Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import static ita.tinybite.global.response.APIResponse.success;

@RestController
@RequestMapping("/api/v1/user/me")
@RequestMapping("/api/v1/user")
public class UserController {

private final UserService userService;
Expand All @@ -18,27 +18,33 @@ public UserController(UserService userService) {
this.userService = userService;
}

@GetMapping
@GetMapping("/me")
public APIResponse<?> getUser() {
return success(userService.getUser());
}
Comment on lines 22 to 24
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

getUser() 메서드에 HTTP 매핑 어노테이션 누락 - 엔드포인트 비활성화됨.

getUser() 메서드에 @GetMapping 어노테이션이 없어서 이 엔드포인트가 HTTP 요청을 받을 수 없습니다. 의도적인 변경인지 확인이 필요합니다. 엔드포인트가 필요하다면 어노테이션을 추가해야 합니다.

+    @GetMapping("/me")
     public APIResponse<?> getUser() {
         return success(userService.getUser());
     }
🤖 Prompt for AI Agents
In src/main/java/ita/tinybite/domain/user/controller/UserController.java around
lines 21-23, the getUser() method is missing an HTTP mapping annotation so the
endpoint is inactive; add an appropriate @GetMapping (e.g., @GetMapping or
@GetMapping("/me") depending on routing conventions) directly above the method,
import org.springframework.web.bind.annotation.GetMapping if needed, and ensure
the path matches any class-level @RequestMapping and security/visibility
requirements.


@PatchMapping
@PatchMapping("/me")
public APIResponse<?> updateUser(@Valid @RequestBody UpdateUserReqDto req) {
userService.updateUser(req);
return success();
}

@PatchMapping("/location")
@PatchMapping("/me/location")
public APIResponse<?> updateLocation(@RequestParam(defaultValue = "37.3623504988728") String latitude,
@RequestParam(defaultValue = "127.117057453619") String longitude) {
userService.updateLocation(latitude, longitude);
return success();
}

@DeleteMapping
@DeleteMapping("/me")
public APIResponse<?> deleteUser() {
userService.deleteUser();
return success();
}

@GetMapping("/nickname/check")
public APIResponse<?> validateNickname(@RequestParam String nickname) {
userService.validateNickname(nickname);
return success();
}
}
8 changes: 4 additions & 4 deletions src/main/java/ita/tinybite/domain/user/entity/User.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,16 @@
public class User extends BaseEntity {

@Id
@GeneratedValue
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Comment("uid")
private Long userId;

@Column(nullable = false, length = 50, unique = true)
private String email;

@Column(nullable = false, length = 30, unique = true)
private String nickname;

@Column(length = 50)
private String phone;

Expand All @@ -36,9 +39,6 @@ public class User extends BaseEntity {
@Column(nullable = false)
private UserStatus status;

@Column(nullable = false, length = 100)
private String nickname;

@Column(nullable = false, length = 100)
private String location;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import ita.tinybite.domain.user.dto.res.UserResDto;
import ita.tinybite.domain.user.entity.User;
import ita.tinybite.domain.user.repository.UserRepository;
import ita.tinybite.global.exception.BusinessException;
import ita.tinybite.global.exception.errorcode.AuthErrorCode;
import ita.tinybite.global.location.LocationService;
import org.springframework.stereotype.Service;

Expand Down Expand Up @@ -43,4 +45,8 @@ public void deleteUser() {
userRepository.delete(securityProvider.getCurrentUser());
}

public void validateNickname(String nickname) {
if(userRepository.existsByNickname(nickname))
throw BusinessException.of(AuthErrorCode.DUPLICATED_NICKNAME);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ public class SwaggerConfig {
@Bean
public OpenAPI openAPI() {
return new OpenAPI()
.servers(List.of(new Server().url("https://growinserver.shop")))
.addSecurityItem(new SecurityRequirement().addList("BearerAuth"))
.components(
new Components()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@
public enum AuthErrorCode implements ErrorCode {

INVALID_PHONE_NUMBER(HttpStatus.BAD_REQUEST, "INVALID_PHONE_NUMBER", "유효하지 않은 번호입니다."),
INVALID_AUTHCODE(HttpStatus.UNAUTHORIZED, "INVALID_AUTHCODE", "인증코드가 만료되었거나, 일치하지 않습니다.")
INVALID_AUTHCODE(HttpStatus.UNAUTHORIZED, "INVALID_AUTHCODE", "인증코드가 일치하지 않습니다."),
EXPIRED_AUTH_CODE(HttpStatus.BAD_REQUEST, "EXPIRED_AUTH_CODE", "인증시간이 만료되었습니다."),

DUPLICATED_NICKNAME(HttpStatus.BAD_REQUEST, "DUPLICATED_NICKNAME", "중복된 닉네임입니다."),

;

private final HttpStatus httpStatus;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package ita.tinybite.global.sms.controller;

import ita.tinybite.global.exception.errorcode.AuthErrorCode;
import ita.tinybite.global.exception.errorcode.TaskErrorCode;
import ita.tinybite.global.response.APIResponse;
import ita.tinybite.global.sms.dto.req.CheckReqDto;
import ita.tinybite.global.sms.dto.req.SendReqDto;
Expand All @@ -11,6 +9,8 @@
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import static ita.tinybite.global.response.APIResponse.success;

@RestController
@RequestMapping("/api/v1/auth/sms")
public class SmsAuthController {
Expand All @@ -24,12 +24,12 @@ public SmsAuthController(SmsAuthService smsAuthService) {
@PostMapping("/send")
public APIResponse<?> send(@RequestBody SendReqDto req) {
smsAuthService.send(req.phone());
return APIResponse.success();
return success();
}

@PostMapping("/check")
public APIResponse<?> check(@RequestBody CheckReqDto req) {
if(smsAuthService.check(req)) return APIResponse.success();
return APIResponse.businessError(AuthErrorCode.INVALID_AUTHCODE);
smsAuthService.check(req);
return success();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public SmsAuthService(SmsService smsService, RedisTemplate<String, String> redis
/**
* 1. 인증코드 생성 <br>
* 2. 주어진 폰번호로 인증코드 전송 <br>
* 3. DB에 {번호, 인증코드}쌍으로 저장 or 메모리에 저장 (ttl 설정 고려하기) <br>
* 3. redis에 {번호, 인증코드}쌍으로 저장 (ttl 설정 고려) <br>
*/
public void send(String phone) {
validatePhoneNumber(phone);
Expand All @@ -40,14 +40,17 @@ public void send(String phone) {
/**
* req.phone으로 redis 조회 <br>
* 조회한 authCode와 요청받은 authcode를 비교 <br>
* 같으면 true, 다르면 false <br>
* 조회된 authCode가 없을 시, 만료 혹은 요청 X <br>
*/
public boolean check(CheckReqDto req) {
public void check(CheckReqDto req) {
validatePhoneNumber(req.phone());

String authCode = redisTemplate.opsForValue().get(req.phone());
if(authCode == null) return false;
return authCode.equals(req.authCode());
if(authCode == null)
throw BusinessException.of(AuthErrorCode.EXPIRED_AUTH_CODE);

if(!authCode.equals(req.authCode()))
throw BusinessException.of(AuthErrorCode.INVALID_AUTHCODE);
}

private void validatePhoneNumber(String phone) {
Expand Down
9 changes: 2 additions & 7 deletions src/main/resources/application-dev.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ spring:

data:
redis:
host: localhost
host: redis
port: 6379

kakao:
Expand All @@ -25,9 +25,4 @@ kakao:

naver:
client-id: ${NAVER_CLIENT_ID}
secret: ${NAVER_CLIENT_SECRET}

jwt:
secret: ${JWT_SECRET}
access-token-validity: ${JWT_ACCESS_TOKEN_VALIDITY}
refresh-token-validity: ${JWT_REFRESH_TOKEN_VALIDITY}
secret: ${NAVER_CLIENT_SECRET}
5 changes: 0 additions & 5 deletions src/main/resources/application-local.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,6 @@ naver:
client-id: ${NAVER_CLIENT_ID}
secret: ${NAVER_CLIENT_SECRET}

jwt:
secret: ${JWT_SECRET}
access-token-validity: ${JWT_ACCESS_TOKEN_VALIDITY}
refresh-token-validity: ${JWT_REFRESH_TOKEN_VALIDITY}

logging:
level:
org.hibernate.SQL: debug
15 changes: 0 additions & 15 deletions src/test/java/ita/tinybite/TinyBiteApplicationTests.java

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package ita.tinybite.domain.user.service;

import ita.tinybite.domain.user.constant.LoginType;
import ita.tinybite.domain.user.constant.UserStatus;
import ita.tinybite.domain.user.dto.req.UpdateUserReqDto;
import ita.tinybite.domain.user.dto.res.UserResDto;
import ita.tinybite.domain.user.entity.User;
import ita.tinybite.domain.user.repository.UserRepository;
import ita.tinybite.domain.user.service.fake.FakeLocationService;
import ita.tinybite.domain.user.service.fake.FakeSecurityProvider;
import ita.tinybite.global.exception.BusinessException;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;

import static org.assertj.core.api.Assertions.*;

@DataJpaTest
class UserServiceTest {

@Autowired
private UserRepository userRepository;

// Fake 객체
private FakeSecurityProvider securityProvider;
private FakeLocationService locationService;

// 테스트 객체
private UserService userService;

@BeforeEach
void setUp() {
securityProvider = new FakeSecurityProvider(userRepository);
locationService = new FakeLocationService();
userService = new UserService(securityProvider, userRepository, locationService);

User user = User.builder()
.email("yyytir777@gmail.com")
.nickname("임원재")
.location("분당구 정자동")
.status(UserStatus.ACTIVE)
.type(LoginType.KAKAO)
.build();

userRepository.save(user);
securityProvider.setCurrentUser(user);
}

@Test
void getUser() {
UserResDto user = userService.getUser();
assertThat(user).isNotNull();
}

@Test
void updateUser() {
// given
UpdateUserReqDto req = new UpdateUserReqDto("updated_nickname");

// when
userService.updateUser(req);

// then
assertThat(securityProvider.getCurrentUser().getNickname()).isEqualTo("updated_nickname");
}

@Test
void updateLocation() {
// given
String latitude = "12.123145"; String longitude = "123.123123";

// when
userService.updateLocation(latitude, longitude);

// then
assertThat(securityProvider.getCurrentUser().getLocation()).isEqualTo(locationService.getLocation(latitude, longitude));
}

@Test
void deleteUser() {
// when
User currentUser = securityProvider.getCurrentUser();
userService.deleteUser();

// then
assertThat(userRepository.findById(currentUser.getUserId())).isEmpty();
}

@Test
void validateNickname() {
assertThatThrownBy(() -> userService.validateNickname("임원재"))
.isInstanceOf(BusinessException.class);

assertThatNoException().isThrownBy(() -> userService.validateNickname("임원재1"));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package ita.tinybite.domain.user.service.fake;

import ita.tinybite.global.location.LocationService;

public class FakeLocationService extends LocationService {

@Override
public String getLocation(String latitude, String longitude) {
return latitude + " " + longitude;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package ita.tinybite.domain.user.service.fake;

import ita.tinybite.domain.auth.service.SecurityProvider;
import ita.tinybite.domain.user.entity.User;
import ita.tinybite.domain.user.repository.UserRepository;

public class FakeSecurityProvider extends SecurityProvider {

private User currentUser;

public FakeSecurityProvider(UserRepository userRepository) {
super(userRepository);
}

public void setCurrentUser(User user) {
currentUser = user;
}

@Override
public User getCurrentUser() {
return currentUser;
}
}
8 changes: 8 additions & 0 deletions src/test/java/ita/tinybite/global/sms/SmsAuthServiceTest.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package ita.tinybite.global.sms;

import ita.tinybite.global.exception.BusinessException;
import ita.tinybite.global.sms.dto.req.CheckReqDto;
import ita.tinybite.global.sms.fake.FakeRedisTemplate;
import ita.tinybite.global.sms.fake.FakeSmsService;
import ita.tinybite.global.sms.service.SmsAuthService;
import org.junit.jupiter.api.Test;
import org.springframework.test.context.ActiveProfiles;


import static org.assertj.core.api.Assertions.*;
Expand Down Expand Up @@ -35,4 +37,10 @@ void should_fail_when_smsAuth_send_with_invalid_phone() {
.isInstanceOf(BusinessException.class);
}
}

@Test
void should_fail_when_smsAuth_send_with_expired_auth_code() {
assertThatThrownBy(() -> smsAuthService.check(new CheckReqDto(SUCCESS_PHONE_NUMBER, "123456")))
.isInstanceOf(BusinessException.class);
}
}
Loading