From a78d304405831743b5ce8dd4e0eccf4c0805dbd3 Mon Sep 17 00:00:00 2001 From: Suhun0331 Date: Sun, 28 Jul 2024 13:35:47 +0900 Subject: [PATCH] =?UTF-8?q?Feat=20:=20=ED=9C=B4=EB=8C=80=ED=8F=B0=20?= =?UTF-8?q?=EC=9D=B8=EC=A6=9D=20=EC=9A=94=EC=B2=AD/=ED=99=95=EC=9D=B8=20AP?= =?UTF-8?q?I=20(application.properties=20=EB=AF=B8=EC=A0=81=EC=9A=A9)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../response/CreateMemberResponse.java | 14 +++ .../response/MemberFieldResponse.java | 14 +++ .../response/MemberInfoResponse.java | 23 ++++ .../controller/response/ResultResponse.java | 16 +++ .../member/dto/MemberPhoneNumberDto.java | 16 +++ .../member/phoneauth/MessageService.java | 112 ++++++++++++++++++ .../server/member/phoneauth/RedisConfig.java | 33 ++++++ .../member/phoneauth/SmsCertification.java | 37 ++++++ .../member/phoneauth/SmsCertificationDto.java | 18 +++ .../server/member/service/MemberInfoTest.java | 99 ++++++++++++++++ 10 files changed, 382 insertions(+) create mode 100644 src/main/java/umc/kkijuk/server/member/controller/response/CreateMemberResponse.java create mode 100644 src/main/java/umc/kkijuk/server/member/controller/response/MemberFieldResponse.java create mode 100644 src/main/java/umc/kkijuk/server/member/controller/response/MemberInfoResponse.java create mode 100644 src/main/java/umc/kkijuk/server/member/controller/response/ResultResponse.java create mode 100644 src/main/java/umc/kkijuk/server/member/dto/MemberPhoneNumberDto.java create mode 100644 src/main/java/umc/kkijuk/server/member/phoneauth/MessageService.java create mode 100644 src/main/java/umc/kkijuk/server/member/phoneauth/RedisConfig.java create mode 100644 src/main/java/umc/kkijuk/server/member/phoneauth/SmsCertification.java create mode 100644 src/main/java/umc/kkijuk/server/member/phoneauth/SmsCertificationDto.java create mode 100644 src/test/java/umc/kkijuk/server/member/service/MemberInfoTest.java diff --git a/src/main/java/umc/kkijuk/server/member/controller/response/CreateMemberResponse.java b/src/main/java/umc/kkijuk/server/member/controller/response/CreateMemberResponse.java new file mode 100644 index 00000000..a7852b3c --- /dev/null +++ b/src/main/java/umc/kkijuk/server/member/controller/response/CreateMemberResponse.java @@ -0,0 +1,14 @@ +package umc.kkijuk.server.member.controller.response; + +import lombok.Data; + +@Data +public class CreateMemberResponse { + private Long id; + private String message; + + public CreateMemberResponse(String message) { + this.message = message; + } + +} diff --git a/src/main/java/umc/kkijuk/server/member/controller/response/MemberFieldResponse.java b/src/main/java/umc/kkijuk/server/member/controller/response/MemberFieldResponse.java new file mode 100644 index 00000000..45dc81fa --- /dev/null +++ b/src/main/java/umc/kkijuk/server/member/controller/response/MemberFieldResponse.java @@ -0,0 +1,14 @@ +package umc.kkijuk.server.member.controller.response; + +import lombok.Data; + +import java.util.List; + +@Data +public class MemberFieldResponse{ + private List field; + + public MemberFieldResponse(List field) { + this.field = field; + } +} diff --git a/src/main/java/umc/kkijuk/server/member/controller/response/MemberInfoResponse.java b/src/main/java/umc/kkijuk/server/member/controller/response/MemberInfoResponse.java new file mode 100644 index 00000000..ec0be718 --- /dev/null +++ b/src/main/java/umc/kkijuk/server/member/controller/response/MemberInfoResponse.java @@ -0,0 +1,23 @@ +package umc.kkijuk.server.member.controller.response; + +import lombok.Data; + +import java.time.LocalDate; + +@Data +public class MemberInfoResponse { + private String email; + private String name; + private String phoneNumber; + private LocalDate birthDate; + + public MemberInfoResponse() { + } + + public MemberInfoResponse(String email, String name, String phoneNumber, LocalDate birthDate) { + this.email = email; + this.name = name; + this.phoneNumber = phoneNumber; + this.birthDate = birthDate; + } +} diff --git a/src/main/java/umc/kkijuk/server/member/controller/response/ResultResponse.java b/src/main/java/umc/kkijuk/server/member/controller/response/ResultResponse.java new file mode 100644 index 00000000..28832503 --- /dev/null +++ b/src/main/java/umc/kkijuk/server/member/controller/response/ResultResponse.java @@ -0,0 +1,16 @@ +package umc.kkijuk.server.member.controller.response; + +import lombok.Data; + +@Data +public class ResultResponse{ + private String message; + + public ResultResponse(){ + + } + + public ResultResponse(String message) { + this.message = message; + } +} diff --git a/src/main/java/umc/kkijuk/server/member/dto/MemberPhoneNumberDto.java b/src/main/java/umc/kkijuk/server/member/dto/MemberPhoneNumberDto.java new file mode 100644 index 00000000..968a6e5c --- /dev/null +++ b/src/main/java/umc/kkijuk/server/member/dto/MemberPhoneNumberDto.java @@ -0,0 +1,16 @@ +package umc.kkijuk.server.member.dto; + +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +public class MemberPhoneNumberDto { + private String phoneNumber; + + @Builder + public MemberPhoneNumberDto(String phoneNumber) { + this.phoneNumber = phoneNumber; + } +} \ No newline at end of file diff --git a/src/main/java/umc/kkijuk/server/member/phoneauth/MessageService.java b/src/main/java/umc/kkijuk/server/member/phoneauth/MessageService.java new file mode 100644 index 00000000..0535bd47 --- /dev/null +++ b/src/main/java/umc/kkijuk/server/member/phoneauth/MessageService.java @@ -0,0 +1,112 @@ +package umc.kkijuk.server.member.phoneauth; + +import lombok.RequiredArgsConstructor; +import net.nurigo.sdk.NurigoApp; +import net.nurigo.sdk.message.model.Message; +import net.nurigo.sdk.message.request.SingleMessageSendingRequest; +import net.nurigo.sdk.message.response.SingleMessageSentResponse; +import net.nurigo.sdk.message.service.DefaultMessageService; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + + +import java.util.HashMap; +import java.util.Random; + +@Service +public class MessageService { + private final SmsCertification smsCertification; + private final DefaultMessageService messageService; + +// @Value("${coolsms.apikey}") +// private String apiKey; +// +// @Value("${coolsms.apisecret}") +// private String apiSecret; + + @Value("$01033236326") + private String fromNumber; + + public MessageService(SmsCertification smsCertification, @Value("${coolsms.api.key}") String apiKey, + @Value("${coolsms.api.secret}") String apiSecret ) { + this.smsCertification = smsCertification; + // 반드시 계정 내 등록된 유효한 API 키, API Secret Key를 입력해주셔야 합니다! + this.messageService = NurigoApp.INSTANCE.initialize(apiKey, apiSecret, "https://api.coolsms.co.kr"); + } + + + + private String createRandomNumber() { + Random rand = new Random(); + String randomNum = ""; + for (int i = 0; i < 4; i++) { + String random = Integer.toString(rand.nextInt(10)); + randomNum += random; + } + + return randomNum; + } + + private HashMap makeParams(String to, String randomNum) { + HashMap params = new HashMap<>(); + params.put("from", fromNumber); + params.put("type", "SMS"); + params.put("app_version", "test app 1.2"); + params.put("to", to); + params.put("text", randomNum); + return params; + } + + + // 인증번호 전송하기 + public SingleMessageSentResponse sendSMS(String phoneNumber) { + Message coolsms = new Message(); + coolsms.setFrom(fromNumber); + coolsms.setTo(phoneNumber); + // 랜덤한 인증 번호 생성 + String randomNum = createRandomNumber(); + System.out.println(randomNum); + coolsms.setText("인증번호: "+randomNum); + + + // 발신 정보 설정 + HashMap params = makeParams(phoneNumber, randomNum); + +// try { +// JSONObject obj = (JSONObject) coolsms.send(params); +// System.out.println(obj.toString()); +// } catch (Exception e) { +// System.out.println(e.getMessage()); +// System.out.println(e.getCode()); +// } + + SingleMessageSentResponse response = this.messageService.sendOne(new SingleMessageSendingRequest(coolsms)); + System.out.println(response); + + + // DB에 발송한 인증번호 저장 + smsCertification.createSmsCertification(phoneNumber,randomNum); + + + return response; + + +// return "문자 전송이 완료되었습니다."; + } + + // 인증 번호 검증 + public String verifySms(SmsCertificationDto requestDto) { + if (isVerify(requestDto)) { + throw new IllegalArgumentException("인증번호가 일치하지 않습니다."); + } + smsCertification.deleteSmsCertification(requestDto.getPhoneNumber()); + + return "인증 완료되었습니다."; + } + + private boolean isVerify(SmsCertificationDto requestDto) { + return !(smsCertification.hasKey(requestDto.getPhoneNumber()) && + smsCertification.getSmsCertification(requestDto.getPhoneNumber()) + .equals(requestDto.getRandomNumber())); + } +} \ No newline at end of file diff --git a/src/main/java/umc/kkijuk/server/member/phoneauth/RedisConfig.java b/src/main/java/umc/kkijuk/server/member/phoneauth/RedisConfig.java new file mode 100644 index 00000000..e94814d1 --- /dev/null +++ b/src/main/java/umc/kkijuk/server/member/phoneauth/RedisConfig.java @@ -0,0 +1,33 @@ +package umc.kkijuk.server.member.phoneauth; + +import lombok.RequiredArgsConstructor; +import org.springframework.boot.autoconfigure.data.redis.RedisProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.repository.configuration.EnableRedisRepositories; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +@Configuration +@RequiredArgsConstructor +@EnableRedisRepositories +public class RedisConfig { + private final RedisProperties redisProperties; + + // lettuce + @Bean + public RedisConnectionFactory redisConnectionFactory() { + return new LettuceConnectionFactory(redisProperties.getHost(), redisProperties.getPort()); + } + + @Bean + public RedisTemplate redisTemplate() { + RedisTemplate redisTemplate = new RedisTemplate<>(); + redisTemplate.setConnectionFactory(redisConnectionFactory()); + redisTemplate.setKeySerializer(new StringRedisSerializer()); + redisTemplate.setValueSerializer(new StringRedisSerializer()); + return redisTemplate; + } +} diff --git a/src/main/java/umc/kkijuk/server/member/phoneauth/SmsCertification.java b/src/main/java/umc/kkijuk/server/member/phoneauth/SmsCertification.java new file mode 100644 index 00000000..8ba0674f --- /dev/null +++ b/src/main/java/umc/kkijuk/server/member/phoneauth/SmsCertification.java @@ -0,0 +1,37 @@ +package umc.kkijuk.server.member.phoneauth; + +import lombok.RequiredArgsConstructor; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.stereotype.Repository; + +import java.time.Duration; + +@RequiredArgsConstructor +@Repository +public class SmsCertification { + private final String PREFIX = "sms:"; // key값이 중복되지 않도록 상수 선언 + private final int LIMIT_TIME = 3 * 60; // 인증번호 유효 시간 + + private final StringRedisTemplate stringRedisTemplate; + + // Redis에 저장 + public void createSmsCertification(String phone, String certificationNumber) { + stringRedisTemplate.opsForValue() + .set(PREFIX + phone, certificationNumber, Duration.ofSeconds(LIMIT_TIME)); + } + + // 휴대전화 번호에 해당하는 인증번호 불러오기 + public String getSmsCertification(String phone) { + return stringRedisTemplate.opsForValue().get(PREFIX + phone); + } + + // 인증 완료 시, 인증번호 Redis에서 삭제 + public void deleteSmsCertification(String phone) { + stringRedisTemplate.delete(PREFIX + phone); + } + + // Redis에 해당 휴대번호로 저장된 인증번호가 존재하는지 확인 + public boolean hasKey(String phone) { + return stringRedisTemplate.hasKey(PREFIX + phone); + } +} \ No newline at end of file diff --git a/src/main/java/umc/kkijuk/server/member/phoneauth/SmsCertificationDto.java b/src/main/java/umc/kkijuk/server/member/phoneauth/SmsCertificationDto.java new file mode 100644 index 00000000..946d1e38 --- /dev/null +++ b/src/main/java/umc/kkijuk/server/member/phoneauth/SmsCertificationDto.java @@ -0,0 +1,18 @@ +package umc.kkijuk.server.member.phoneauth; + +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +public class SmsCertificationDto { + private String phoneNumber; + private String randomNumber; + + @Builder + public SmsCertificationDto(String phoneNumber, String randomNumber) { + this.phoneNumber = phoneNumber; + this.randomNumber = randomNumber; + } +} diff --git a/src/test/java/umc/kkijuk/server/member/service/MemberInfoTest.java b/src/test/java/umc/kkijuk/server/member/service/MemberInfoTest.java new file mode 100644 index 00000000..69afe43d --- /dev/null +++ b/src/test/java/umc/kkijuk/server/member/service/MemberInfoTest.java @@ -0,0 +1,99 @@ +package umc.kkijuk.server.member.service; + +import org.junit.jupiter.api.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.test.annotation.DirtiesContext; +import umc.kkijuk.server.member.controller.response.MemberInfoResponse; +import umc.kkijuk.server.member.controller.response.ResultResponse; +import umc.kkijuk.server.member.domain.MarketingAgree; +import umc.kkijuk.server.member.domain.Member; +import umc.kkijuk.server.member.domain.State; +import umc.kkijuk.server.member.dto.MemberInfoChangeDto; +import umc.kkijuk.server.member.repository.MemberJpaRepository; +import umc.kkijuk.server.common.LoginUser; + +import java.time.LocalDate; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +public class MemberInfoTest { + + + @Autowired + private TestRestTemplate restTemplate; + + @Autowired + private MemberJpaRepository memberJpaRepository; + + @BeforeEach + public void setup() { + memberJpaRepository.deleteAll(); + } + + @Test + @DirtiesContext(methodMode = DirtiesContext.MethodMode.AFTER_METHOD) + public void 내정보_조회() { + // Given + Member member1 = new Member("asd@naver.com", "홍길동", "010-1234-5678", + LocalDate.parse("1999-03-31"), "passwordTest", MarketingAgree.BOTH, State.ACTIVATE); + memberJpaRepository.save(member1); + System.out.println("test1"); + // When + ResponseEntity response = restTemplate.getForEntity("/member/myPage/info", MemberInfoResponse.class); + + // Then + assertEquals(HttpStatus.OK, response.getStatusCode()); + + MemberInfoResponse body = response.getBody(); + assertNotNull(body); + assertEquals("asd@naver.com", body.getEmail()); + assertEquals("홍길동", body.getName()); + assertEquals("010-1234-5678", body.getPhoneNumber()); + assertEquals(LocalDate.parse("1999-03-31"), body.getBirthDate()); + } + + @Test + @DirtiesContext(methodMode = DirtiesContext.MethodMode.AFTER_METHOD) + public void 내정보_수정() { + // Given + Member member1 = new Member("asd@naver.com", "홍길동", "010-1234-5678", + LocalDate.parse("1999-03-31"), "passwordTest", MarketingAgree.BOTH, State.ACTIVATE); + memberJpaRepository.save(member1); + System.out.println("test2"); + + + MemberInfoChangeDto changeDto = MemberInfoChangeDto.builder() + .phoneNumber("010-5678-1234") + .birthDate(LocalDate.parse("2000-01-01")) + .marketingAgree(MarketingAgree.EMAIL) + .build(); + + HttpEntity requestEntity = new HttpEntity<>(changeDto); + + // When + ResponseEntity response = restTemplate.exchange("/member/myPage/info", HttpMethod.PUT, requestEntity, ResultResponse.class); + + // Then + assertEquals(HttpStatus.OK, response.getStatusCode()); + ResultResponse body = response.getBody(); + assertNotNull(body); + assertEquals("information update success", body.getMessage()); + + // Verify changes + Member updatedMember = memberJpaRepository.findById(member1.getId()).orElse(null); + assertNotNull(updatedMember); + assertEquals("010-5678-1234", updatedMember.getPhoneNumber()); + assertEquals(LocalDate.parse("2000-01-01"), updatedMember.getBirthDate()); + assertEquals(MarketingAgree.EMAIL, updatedMember.getMarketingAgree()); + } + +}