Skip to content

Commit

Permalink
feat: (#95) origin dev로부터 최신화 진행
Browse files Browse the repository at this point in the history
  • Loading branch information
tjdtls690 committed Jul 28, 2023
2 parents 7b5e0b7 + 57fac6d commit 50380e4
Show file tree
Hide file tree
Showing 209 changed files with 4,997 additions and 1,444 deletions.
20 changes: 19 additions & 1 deletion backend/src/main/java/com/votogether/config/OpenAPIConfig.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
package com.votogether.config;

import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.security.SecurityRequirement;
import io.swagger.v3.oas.models.security.SecurityScheme;
import io.swagger.v3.oas.models.security.SecurityScheme.In;
import io.swagger.v3.oas.models.security.SecurityScheme.Type;
import io.swagger.v3.oas.models.servers.Server;
import java.util.List;
import org.springframework.beans.factory.annotation.Value;
Expand Down Expand Up @@ -37,9 +42,22 @@ public OpenAPI openAPI() {
.version("v1.0.0")
.description("보투게더 API");

final SecurityScheme securityScheme = new SecurityScheme()
.type(Type.HTTP)
.in(In.HEADER)
.name("Authorization")
.scheme("bearer")
.bearerFormat("JWT")
.description("Bearer JWT");

final SecurityRequirement securityRequirement = new SecurityRequirement()
.addList("bearerAuth");

return new OpenAPI()
.info(info)
.servers(List.of(devServer, prodServer));
.servers(List.of(devServer, prodServer))
.components(new Components().addSecuritySchemes("bearerAuth", securityScheme))
.security(List.of(securityRequirement));
}

}
11 changes: 2 additions & 9 deletions backend/src/main/java/com/votogether/config/WebMvcConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
Expand Down Expand Up @@ -33,14 +32,8 @@ public void addArgumentResolvers(final List<HandlerMethodArgumentResolver> resol
public void addCorsMappings(final CorsRegistry registry) {
registry.addMapping("/**")
.allowedHeaders("*")
.allowedOrigins(origins)
.allowedMethods(
HttpMethod.POST.name(),
HttpMethod.GET.name(),
HttpMethod.PATCH.name(),
HttpMethod.PUT.name(),
HttpMethod.DELETE.name()
)
.allowedOrigins("*")
.allowedMethods("*")
.exposedHeaders(HttpHeaders.LOCATION);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public LoginResponse register(final String code) {
final Member member = Member.from(response);
final Member registeredMember = memberService.register(member);
final String token = tokenProcessor.generateToken(registeredMember);
return new LoginResponse(token, member.getNickname());
return new LoginResponse(token, registeredMember.getNickname().getValue());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,19 @@ public class KakaoOAuthClient {
private final MultiValueMap<String, String> info = new LinkedMultiValueMap<>();

public String getAccessToken(final String code) {
info.remove("code");
info.add("code", code);

final OAuthAccessTokenResponse response = restTemplate.postForObject(
final HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);

final HttpEntity<MultiValueMap<String, String>> httpEntity = new HttpEntity<>(info, headers);

final OAuthAccessTokenResponse response = restTemplate.postForEntity(
"https://kauth.kakao.com/oauth/token",
info,
httpEntity,
OAuthAccessTokenResponse.class
);
).getBody();
return response.accessToken();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.votogether.domain.category.dto.response.CategoryResponse;
import com.votogether.domain.category.service.CategoryService;
import com.votogether.domain.member.entity.Member;
import com.votogether.global.jwt.Auth;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
Expand Down Expand Up @@ -41,7 +42,7 @@ public ResponseEntity<List<CategoryResponse>> getAllCategories() {
@ApiResponse(responseCode = "404", description = "해당 카테고리가 존재하지 않아 추가 실패"),
})
@PostMapping("/{categoryId}/like")
public ResponseEntity<Void> addFavoriteCategory(final Member member, @PathVariable final Long categoryId) {
public ResponseEntity<Void> addFavoriteCategory(@Auth final Member member, @PathVariable final Long categoryId) {
categoryService.addFavoriteCategory(member, categoryId);
return ResponseEntity.status(HttpStatus.CREATED).build();
}
Expand All @@ -53,15 +54,15 @@ public ResponseEntity<Void> addFavoriteCategory(final Member member, @PathVariab
@ApiResponse(responseCode = "404", description = "해당 카테고리가 존재하지 않아 삭제 실패")
})
@DeleteMapping("/{categoryId}/like")
public ResponseEntity<Void> removeFavoriteCategory(final Member member, @PathVariable final Long categoryId) {
public ResponseEntity<Void> removeFavoriteCategory(@Auth final Member member, @PathVariable final Long categoryId) {
categoryService.removeFavoriteCategory(member, categoryId);
return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
}

@Operation(summary = "회원으로 모든 카테고리 목록 조회하기", description = "회원의 선호하는 카테고리와 전체 카테고리 목록을 조회한다.")
@ApiResponse(responseCode = "200", description = "조회 성공")
@GetMapping
public ResponseEntity<List<CategoryResponse>> getAllCategories(final Member member) {
public ResponseEntity<List<CategoryResponse>> getAllCategories(@Auth final Member member) {
final List<CategoryResponse> categories = categoryService.getAllCategories(member);
return ResponseEntity.status(HttpStatus.OK).body(categories);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package com.votogether.domain.member.controller;

import com.votogether.domain.member.dto.MemberInfoResponse;
import com.votogether.domain.member.dto.MemberNicknameUpdateRequest;
import com.votogether.domain.member.entity.Member;
import com.votogether.domain.member.service.MemberService;
import com.votogether.global.jwt.Auth;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@Tag(name = "회원", description = "회원 API")
@RequiredArgsConstructor
@RequestMapping("/members")
@RestController
public class MemberController {

private final MemberService memberService;

@Operation(summary = "회원 정보 조회", description = "회원 정보를 반환한다.")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "정보 조회 성공"),
@ApiResponse(responseCode = "400", description = "올바르지 않은 요청")
})
@GetMapping("/me")
public ResponseEntity<MemberInfoResponse> findMemberInfo(@Auth final Member member) {
return ResponseEntity.ok(memberService.findMemberInfo(member));
}

@Operation(summary = "회원 닉네임 변경", description = "회원 닉네임을 변경한다.")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "닉네임 변경 성공"),
@ApiResponse(responseCode = "400", description = "올바르지 않은 변경할 닉네임 요청")
})
@PatchMapping("/me/nickname")
public ResponseEntity<Void> changeNickname(
@Auth final Member member,
@Valid @RequestBody final MemberNicknameUpdateRequest request
) {
memberService.changeNickname(member, request.nickname());
return ResponseEntity.ok().build();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.votogether.domain.member.dto;

public record MemberInfoResponse(
String nickname,
Integer point,
int postCount,
int voteCount
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.votogether.domain.member.dto;

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;

@Schema(description = "닉네임 변경 요청")
public record MemberNicknameUpdateRequest(
@Schema(description = "변경할 닉네임", example = "jeomxon")
@NotBlank(message = "닉네임은 빈값 혹은 공백이 포함될 수 없습니다.")
String nickname
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.votogether.domain.auth.dto.KakaoMemberResponse;
import com.votogether.domain.common.BaseEntity;
import jakarta.persistence.Column;
import jakarta.persistence.Embedded;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
Expand All @@ -23,8 +24,8 @@ public class Member extends BaseEntity {
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(length = 15, unique = true, nullable = false)
private String nickname;
@Embedded
private Nickname nickname;

@Enumerated(value = EnumType.STRING)
@Column(length = 20, nullable = false)
Expand Down Expand Up @@ -56,7 +57,7 @@ private Member(
final String socialId,
final Integer point
) {
this.nickname = nickname;
this.nickname = new Nickname(nickname);
this.gender = gender;
this.ageRange = ageRange;
this.birthday = birthday;
Expand All @@ -82,4 +83,8 @@ public void plusPoint(final int point) {
this.point = this.point + point;
}

public void changeNickname(final String nickname) {
this.nickname = new Nickname(nickname);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.votogether.domain.member.entity;

import com.votogether.domain.member.exception.MemberExceptionType;
import com.votogether.exception.BadRequestException;
import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
import java.util.regex.Pattern;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;

@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
@Embeddable
public class Nickname {

private static final int MINIMUM_NICKNAME_LENGTH = 2;
private static final int MAXIMUM_NICKNAME_LENGTH = 16;

@Column(name = "nickname", length = 15, unique = true, nullable = false)
private String value;

public Nickname(final String nickname) {
validateNickname(nickname);
this.value = nickname;
}

private void validateNickname(final String nickname) {
if (nickname.length() < MINIMUM_NICKNAME_LENGTH || nickname.length() > MAXIMUM_NICKNAME_LENGTH) {
throw new BadRequestException(MemberExceptionType.INVALID_NICKNAME_LENGTH);
}
if (!Pattern.matches("^[가-힣a-zA-Z0-9]+$", nickname)) {
throw new BadRequestException(MemberExceptionType.INVALID_NICKNAME_LETTER);
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.votogether.domain.member.exception;

import com.votogether.exception.ExceptionType;
import lombok.Getter;

@Getter
public enum MemberExceptionType implements ExceptionType {

INVALID_NICKNAME_LENGTH(800, "닉네임의 길이가 올바르지 않습니다."),
INVALID_NICKNAME_LETTER(801, "닉네임에 들어갈 수 없는 문자가 포함되어 있습니다."),
ALREADY_EXISTENT_NICKNAME(802, "이미 중복된 닉네임이 존재합니다."),
NONEXISTENT_MEMBER(803, "해당 회원이 존재하지 않습니다.");

private final int code;
private final String message;

MemberExceptionType(final int code, final String message) {
this.code = code;
this.message = message;
}

}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.votogether.domain.member.repository;

import com.votogether.domain.member.entity.Member;
import com.votogether.domain.member.entity.Nickname;
import com.votogether.domain.member.entity.SocialType;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
Expand All @@ -9,4 +10,6 @@ public interface MemberRepository extends JpaRepository<Member, Long> {

Optional<Member> findBySocialIdAndSocialType(final String socialId, final SocialType socialType);

boolean existsByNickname(final Nickname nickname);

}
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
package com.votogether.domain.member.service;

import com.votogether.domain.member.dto.MemberInfoResponse;
import com.votogether.domain.member.entity.Member;
import com.votogether.domain.member.entity.Nickname;
import com.votogether.domain.member.exception.MemberExceptionType;
import com.votogether.domain.member.repository.MemberRepository;
import com.votogether.domain.post.repository.PostRepository;
import com.votogether.domain.vote.repository.VoteRepository;
import com.votogether.exception.BadRequestException;
import com.votogether.exception.NotFoundException;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
Expand All @@ -12,6 +19,8 @@
public class MemberService {

private final MemberRepository memberRepository;
private final PostRepository postRepository;
private final VoteRepository voteRepository;

@Transactional
public Member register(final Member member) {
Expand All @@ -25,7 +34,33 @@ public Member register(final Member member) {
@Transactional(readOnly = true)
public Member findById(final Long memberId) {
return memberRepository.findById(memberId)
.orElseThrow(() -> new IllegalArgumentException("해당 Id를 가진 회원은 존재하지 않습니다."));
.orElseThrow(() -> new NotFoundException(MemberExceptionType.NONEXISTENT_MEMBER));
}

@Transactional(readOnly = true)
public MemberInfoResponse findMemberInfo(final Member member) {
final int numberOfPosts = postRepository.countByMember(member);
final int numberOfVotes = voteRepository.countByMember(member);

return new MemberInfoResponse(
member.getNickname().getValue(),
member.getPoint(),
numberOfPosts,
numberOfVotes
);
}

@Transactional
public void changeNickname(final Member member, final String nickname) {
validateExistentNickname(nickname);
member.changeNickname(nickname);
}

private void validateExistentNickname(final String nickname) {
final boolean isExist = memberRepository.existsByNickname(new Nickname(nickname));
if (isExist) {
throw new BadRequestException(MemberExceptionType.ALREADY_EXISTENT_NICKNAME);
}
}

}
Loading

0 comments on commit 50380e4

Please sign in to comment.