Skip to content
Open
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
3 changes: 3 additions & 0 deletions umc9th/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,6 @@ out/

### VS Code ###
.vscode/

# Spring Boot application configuration
application.yml
43 changes: 43 additions & 0 deletions umc9th/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,51 @@ dependencies {
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'

// QueryDSL : OpenFeign
implementation "io.github.openfeign.querydsl:querydsl-jpa:7.0"
implementation "io.github.openfeign.querydsl:querydsl-core:7.0"
annotationProcessor "io.github.openfeign.querydsl:querydsl-apt:7.0:jpa"
annotationProcessor "jakarta.persistence:jakarta.persistence-api"
annotationProcessor "jakarta.annotation:jakarta.annotation-api"

// Swagger
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.13'
implementation 'org.springdoc:spring doc-openapi-starter-webmvc-api:2.8.13'

// Validation
implementation 'org.springframework.boot:spring-boot-starter-validation'

// Security
implementation 'org.springframework.boot:spring-boot-starter-security'
testImplementation 'org.springframework.security:spring-security-test'

// Jwt
implementation 'io.jsonwebtoken:jjwt-api:0.12.3'
implementation 'io.jsonwebtoken:jjwt-impl:0.12.3'
implementation 'io.jsonwebtoken:jjwt-jackson:0.12.3'
implementation 'org.springframework.boot:spring-boot-configuration-processor'
}

tasks.named('test') {
useJUnitPlatform()
}

// QueryDSL 관련 설정
// generated/querydsl 폴더 생성 & 삽입
def querydslDir = layout.buildDirectory.dir("generated/querydsl").get().asFile

// 소스 세트에 생성 경로 추가 (구체적인 경로 지정)
sourceSets {
main.java.srcDirs += [ querydslDir ]
}

// 컴파일 시 생성 경로 지정
tasks.withType(JavaCompile).configureEach {
options.generatedSourceOutputDirectory.set(querydslDir)
}

// clean 태스크에 생성 폴더 삭제 로직 추가
clean.doLast {
file(querydslDir).deleteDir()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.example.umc9th.auth.cotroller;

import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/auth")
@RequiredArgsConstructor
public class KakaoAuthController {

private final KakaoAuthService kakaoAuthService;

@PostMapping("/kakao")
public JwtResponse kakaoLogin(
@RequestHeader("Authorization") String authorization
) {
String kakaoAccessToken = authorization.replace("Bearer ", "");
return kakaoAuthService.login(kakaoAccessToken);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.example.umc9th.auth.kakao;

public class KakaoApiClient {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.example.umc9th.auth.kakao.dto;

public class KakaoAccount {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.example.umc9th.auth.kakao.dto;

public class KakaoUserInfo {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.example.umc9th.auth.service;

import com.example.umc9th.auth.kakao.KakaoApiClient;
import com.example.umc9th.auth.kakao.dto.KakaoUserInfo;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
@Transactional
public class KakaoAuthService {

private final KakaoApiClient kakaoApiClient;
private final UserRepository userRepository;
private final JwtProvider jwtProvider; // ✅ 기존 것 그대로 사용

public JwtResponse login(String kakaoAccessToken) {

KakaoUserInfo kakaoUser = kakaoApiClient.getUserInfo(kakaoAccessToken);

String providerId = kakaoUser.getId().toString();
String email = kakaoUser.getKakaoAccount() != null
? kakaoUser.getKakaoAccount().getEmail()
: null;

User user = userRepository
.findByProviderAndProviderId(Provider.KAKAO, providerId)
.orElseGet(() ->
userRepository.save(
User.createKakaoUser(email, providerId)
)
);

String jwt = jwtProvider.createAccessToken(user);

return new JwtResponse(jwt);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.example.umc9th.domain.member.controller;

import com.example.umc9th.domain.member.dto.MemberReqDTO;
import com.example.umc9th.domain.member.dto.MemberResDTO;
import com.example.umc9th.domain.member.exception.code.MemberSuccessCode;
import com.example.umc9th.domain.member.service.command.MemberCommandService;
import com.example.umc9th.domain.member.service.query.MemberQueryService;
import com.example.umc9th.global.apiPayload.ApiResponse;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequiredArgsConstructor
public class MemberController {

private final MemberCommandService memberCommandService;
private final MemberQueryService memberQueryService;


// 회원가입
@PostMapping("/sign-up")
public ApiResponse<MemberResDTO.JoinDTO> signUp(
@RequestBody @Valid MemberReqDTO.JoinDTO dto
){
return ApiResponse.onSuccess(MemberSuccessCode.FOUND, memberCommandService.signup(dto));
}

// 로그인
@PostMapping("/login")
public ApiResponse<MemberResDTO.LoginDTO> login(
@RequestBody @Valid MemberReqDTO.LoginDTO dto
){
return ApiResponse.onSuccess(MemberSuccessCode.FOUND, memberQueryService.login(dto));
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.example.umc9th.domain.member.converter;

import com.example.umc9th.domain.member.dto.MemberReqDTO;
import com.example.umc9th.domain.member.dto.MemberResDTO;
import com.example.umc9th.domain.member.entity.Member;
import com.example.umc9th.domain.member.enums.Role;

public class MemberConverter {

// Entity -> DTO
public static MemberResDTO.JoinDTO toJoinDTO(
Member member
){
return MemberResDTO.JoinDTO.builder()
.memberId(member.getId())
.createAt(member.getCreatedAt())
.build();
}

// DTO, Salted Password, Role -> Entity
public static Member toMember(
MemberReqDTO.JoinDTO dto,
String password,
Role role
){
return Member.builder()
.name(dto.name())
.email(dto.email()) // 추가된 코드
.password(password) // 추가된 코드
.role(role) // 추가된 코드
.phone(dto.phone())
.birth(dto.birth())
.address(dto.address())
.gender(dto.gender())
.build();
}

public static MemberResDTO.LoginDTO toLoginDTO(
Member member,
String accessToken
) {
return MemberResDTO.LoginDTO.builder()
.memberId(member.getId())
.accessToken(accessToken)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package com.example.umc9th.domain.member.dto;

import com.example.umc9th.domain.member.entity.Member;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.Collection;
import java.util.List;

@RequiredArgsConstructor
public class CustomUserDetails implements UserDetails {

private final Member member;

@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
// ROLE_ADMIN / ROLE_USER 그대로 사용
return List.of(
new SimpleGrantedAuthority(member.getRole().name())
);
}

@Override
public String getPassword() {
return member.getPassword();
}

@Override
public String getUsername() {
// 이메일을 username으로 사용
return member.getEmail();
}

// 아래는 기본 true 처리 (안 하면 인증 실패할 수 있음)
@Override
public boolean isAccountNonExpired() {
return true;
}

@Override
public boolean isAccountNonLocked() {
return true;
}

@Override
public boolean isCredentialsNonExpired() {
return true;
}

@Override
public boolean isEnabled() {
return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.example.umc9th.domain.member.dto;

import com.example.umc9th.domain.member.enums.Gender;
import com.example.umc9th.global.annotation.ExistFoods;
import jakarta.validation.constraints.NotBlank;

import java.time.LocalDate;
import java.util.List;

public class MemberReqDTO {

public record JoinDTO(
String name,
Gender gender,
LocalDate birth,
String address,
String email,
String password,
String phone,
@ExistFoods
List<Long> preferCategory
){}

// 로그인
public record LoginDTO(
@NotBlank
String email,
@NotBlank
String password
){}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.example.umc9th.domain.member.dto;

import jakarta.validation.constraints.NotBlank;
import lombok.Builder;

import java.time.LocalDateTime;

public class MemberResDTO {

@Builder
public record JoinDTO(
Long memberId,
LocalDateTime createAt
){}

// 로그인
@Builder
public record LoginDTO(
Long memberId,
String accessToken
){}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ public class Interest {
@Enumerated(EnumType.STRING)
private FoodName name;

//연관 관계
@OneToMany(mappedBy = "interest")
private List<MemberInterest> memberInterests = new ArrayList<>();
// //연관 관계
// @OneToMany(mappedBy = "interest")
// private List<MemberInterest> memberInterests = new ArrayList<>();


}
Loading