From d95c8dc37262bf09d4b67f64d1b3cf3163001389 Mon Sep 17 00:00:00 2001 From: sonsumin Date: Tue, 19 Nov 2024 10:37:18 +0900 Subject: [PATCH 01/73] =?UTF-8?q?=E2=9E=95Feat:=20=EC=9D=98=EC=A1=B4?= =?UTF-8?q?=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 42 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/build.gradle b/build.gradle index 7088e0c..ee776e4 100644 --- a/build.gradle +++ b/build.gradle @@ -13,12 +13,6 @@ java { } } -configurations { - compileOnly { - extendsFrom annotationProcessor - } -} - repositories { mavenCentral() } @@ -26,14 +20,50 @@ repositories { dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-security' + implementation 'org.springframework.security:spring-security-oauth2-client' + implementation 'org.springframework.boot:spring-boot-starter-validation' implementation 'org.springframework.boot:spring-boot-starter-web' + + // DB + runtimeOnly 'com.mysql:mysql-connector-j:8.3.0' + runtimeOnly 'com.h2database:h2' + + // p6spy 적용 + implementation 'com.github.gavlyukovskiy:p6spy-spring-boot-starter:1.7.1' compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.springframework.security:spring-security-test' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + + // S3 + implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE' + + // Swagger3 적용 + implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.4' + + // JWT + implementation 'com.auth0:java-jwt:4.4.0' + implementation 'io.jsonwebtoken:jjwt-api:0.11.5' + implementation 'io.jsonwebtoken:jjwt-impl:0.11.5' + implementation 'io.jsonwebtoken:jjwt-jackson:0.11.5' + } tasks.named('test') { useJUnitPlatform() } + +//plain jar 생성 방지 +tasks.named("jar") { + enabled = false +} + +tasks.register('copyExternalProperties', Copy) { + from "./config" + include "*.yml" + into "src/main/resources" +} + +processResources.dependsOn('copyExternalProperties') +processTestResources.dependsOn('copyExternalProperties') \ No newline at end of file From 2c5c7e33975cc2dd7c239c65746e88bcd25197d6 Mon Sep 17 00:00:00 2001 From: sonsumin Date: Tue, 19 Nov 2024 22:54:00 +0900 Subject: [PATCH 02/73] =?UTF-8?q?[#2]=E2=9C=A8Feat:=20cors=20=EB=B0=8F=20?= =?UTF-8?q?=EA=B6=8C=ED=95=9C=20url=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/auth/config/SecurityConfig.java | 134 ++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 src/main/java/univ/yesummit/global/auth/config/SecurityConfig.java diff --git a/src/main/java/univ/yesummit/global/auth/config/SecurityConfig.java b/src/main/java/univ/yesummit/global/auth/config/SecurityConfig.java new file mode 100644 index 0000000..bcdc210 --- /dev/null +++ b/src/main/java/univ/yesummit/global/auth/config/SecurityConfig.java @@ -0,0 +1,134 @@ +package univ.yesummit.global.auth.config; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +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.config.annotation.web.configurers.HeadersConfigurer; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.CorsConfigurationSource; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import org.springframework.web.filter.OncePerRequestFilter; + +@Slf4j +@Configuration +@EnableWebSecurity +@RequiredArgsConstructor +@EnableMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true) +public class SecurityConfig { + + + + + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + + return http + // csrf 차단 + .csrf(AbstractHttpConfigurer::disable) + + // cors 설정 + .cors(cors -> cors.configurationSource(corsConfigurationSource())) + + // 시큐리티 기본 로그인 비활성화 + .formLogin(AbstractHttpConfigurer::disable) + .httpBasic(AbstractHttpConfigurer::disable) + + // iframe 차단 + .headers(header -> header.frameOptions( + HeadersConfigurer.FrameOptionsConfig::sameOrigin + )) + + // session 사용 중지 + .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) + + // 권한 url 설정 + .authorizeHttpRequests(req -> req + .requestMatchers("/swagger-ui/**").permitAll() + .requestMatchers("/v3/api-docs/**").permitAll() + .anyRequest().permitAll()) + + // oauth2Login 설정 + .oauth2Login((oauth2) -> oauth2 + .authorizationEndpoint(authorization -> + authorization.baseUri("/oauth2/authorization")) // 인증 요청 엔드포인트 설정 + .redirectionEndpoint(redirection -> + redirection.baseUri("/login/oauth2/code/*")) // 리다이렉트 엔드포인트 설정 + + // logout 설정 + .logout(logout -> logout + .logoutUrl("/logout") + .logoutSuccessUrl("/") + .invalidateHttpSession(true) + .deleteCookies("JSESSIONID")) + + .build(); + } + + @Value("${cors.allowed-origins:http://localhost:3000}") + private List allowOriginList; + + @Bean + public CorsConfigurationSource corsConfigurationSource() { + CorsConfiguration configuration = new CorsConfiguration(); + configuration.setAllowedOrigins(allowOriginList); // 허용할 Origin 추가 + configuration.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS")); + configuration.setAllowedHeaders(List.of("*")); + configuration.setAllowCredentials(true); + configuration.addExposedHeader("Authorization"); + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", configuration); + return source; + } + + public class JwtAuthenticationFilter extends OncePerRequestFilter { + private final JwtUtils jwtUtils; + + public JwtAuthenticationFilter(JwtUtils jwtUtils) { + this.jwtUtils = jwtUtils; + } + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + String token = resolveToken(request); // 요청에서 토큰 추출 + if (token != null && jwtUtils.isValid(token)) { + try { + // 토큰에서 memberId 추출 + Optional memberId = jwtUtils.extractMemberId(token); + + // 인증 객체 생성 + Authentication authentication = new UsernamePasswordAuthenticationToken(memberId, null, List.of()); + + // SecurityContext에 인증 객체 저장 + SecurityContextHolder.getContext().setAuthentication(authentication); + } catch (Exception e) { + log.error("JWT 인증 실패: {}", e.getMessage()); + } + } + filterChain.doFilter(request, response); + } + + private String resolveToken(HttpServletRequest request) { + String bearerToken = request.getHeader("Authorization"); + if (bearerToken != null && bearerToken.startsWith("Bearer ")) { + return bearerToken.substring(7); + } + return null; + } + } +} From 32e958ea0190ae232e3aceff13322b8659bb2437 Mon Sep 17 00:00:00 2001 From: sonsumin Date: Tue, 19 Nov 2024 22:55:35 +0900 Subject: [PATCH 03/73] =?UTF-8?q?[#2]=E2=9C=A8Feat:=20=EC=B9=B4=EC=B9=B4?= =?UTF-8?q?=EC=98=A4=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20url=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/auth/config/SecurityConfig.java | 30 +++++++++++++------ 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/src/main/java/univ/yesummit/global/auth/config/SecurityConfig.java b/src/main/java/univ/yesummit/global/auth/config/SecurityConfig.java index bcdc210..e69a266 100644 --- a/src/main/java/univ/yesummit/global/auth/config/SecurityConfig.java +++ b/src/main/java/univ/yesummit/global/auth/config/SecurityConfig.java @@ -24,6 +24,14 @@ import org.springframework.web.cors.CorsConfigurationSource; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.OncePerRequestFilter; +import univ.yesummit.global.auth.util.JwtUtils; +import univ.yesummit.global.oauth.OAuth2MemberService; +import univ.yesummit.global.oauth.OAuth2SuccessHandler; + +import java.io.IOException; +import java.util.List; +import java.util.Optional; + @Slf4j @Configuration @@ -32,8 +40,14 @@ @EnableMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true) public class SecurityConfig { + private final OAuth2MemberService oAuth2MemberService; + private final OAuth2SuccessHandler oAuth2SuccessHandler; + private final JwtUtils jwtUtils; - + @Bean + public JwtAuthenticationFilter jwtAuthenticationFilter() { + return new JwtAuthenticationFilter(jwtUtils); + } @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { @@ -61,6 +75,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti .authorizeHttpRequests(req -> req .requestMatchers("/swagger-ui/**").permitAll() .requestMatchers("/v3/api-docs/**").permitAll() + .requestMatchers("/oauth2/authorization/**", "/login/oauth2/code/**").permitAll() .anyRequest().permitAll()) // oauth2Login 설정 @@ -69,15 +84,12 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti authorization.baseUri("/oauth2/authorization")) // 인증 요청 엔드포인트 설정 .redirectionEndpoint(redirection -> redirection.baseUri("/login/oauth2/code/*")) // 리다이렉트 엔드포인트 설정 + .userInfoEndpoint((userInfoEndpointConfig) -> + userInfoEndpointConfig.userService(oAuth2MemberService)) + .successHandler(oAuth2SuccessHandler)) + .addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class) - // logout 설정 - .logout(logout -> logout - .logoutUrl("/logout") - .logoutSuccessUrl("/") - .invalidateHttpSession(true) - .deleteCookies("JSESSIONID")) - - .build(); + } @Value("${cors.allowed-origins:http://localhost:3000}") From 06e347b2d3cf928c8ff3abea855b621c55ffe576 Mon Sep 17 00:00:00 2001 From: sonsumin Date: Tue, 19 Nov 2024 22:55:52 +0900 Subject: [PATCH 04/73] =?UTF-8?q?[#2]=E2=9C=A8Feat:=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=95=84=EC=9B=83=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../univ/yesummit/global/auth/config/SecurityConfig.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/java/univ/yesummit/global/auth/config/SecurityConfig.java b/src/main/java/univ/yesummit/global/auth/config/SecurityConfig.java index e69a266..e386f7e 100644 --- a/src/main/java/univ/yesummit/global/auth/config/SecurityConfig.java +++ b/src/main/java/univ/yesummit/global/auth/config/SecurityConfig.java @@ -89,7 +89,14 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti .successHandler(oAuth2SuccessHandler)) .addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class) - + // logout 설정 + .logout(logout -> logout + .logoutUrl("/logout") + .logoutSuccessUrl("/") + .invalidateHttpSession(true) + .deleteCookies("JSESSIONID")) + + .build(); } @Value("${cors.allowed-origins:http://localhost:3000}") From 40776b38225003bffda0250c906dff92adcbd798 Mon Sep 17 00:00:00 2001 From: sonsumin Date: Tue, 19 Nov 2024 22:56:52 +0900 Subject: [PATCH 05/73] =?UTF-8?q?[#2]=E2=9C=A8Feat:=20cors=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20Config?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/auth/config/WebMvcConfig.java | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 src/main/java/univ/yesummit/global/auth/config/WebMvcConfig.java diff --git a/src/main/java/univ/yesummit/global/auth/config/WebMvcConfig.java b/src/main/java/univ/yesummit/global/auth/config/WebMvcConfig.java new file mode 100644 index 0000000..a7c675c --- /dev/null +++ b/src/main/java/univ/yesummit/global/auth/config/WebMvcConfig.java @@ -0,0 +1,30 @@ +package univ.yesummit.global.auth.config; + +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import univ.yesummit.global.resolver.AuthArgumentResolver; + +import java.util.List; + +@Configuration +@EnableWebMvc +@RequiredArgsConstructor +public class WebMvcConfig implements WebMvcConfigurer { + + private final AuthArgumentResolver authArgumentResolver; + + @Override + public void addCorsMappings(final CorsRegistry registry ){ + registry.addMapping("/**") + .allowedOriginPatterns("*") + .allowedMethods("PATCH","GET","POST","PUT","DELETE","HEAD","OPTIONS") + .allowedHeaders("*") + .allowCredentials(true); + } + + +} \ No newline at end of file From ab47ac50c0f1f4cf25fb0fd0cb3898adec08d83d Mon Sep 17 00:00:00 2001 From: sonsumin Date: Tue, 19 Nov 2024 22:57:11 +0900 Subject: [PATCH 06/73] =?UTF-8?q?[#2]=E2=9C=A8Feat:=20ArgumentResolver=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/univ/yesummit/global/auth/config/WebMvcConfig.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/univ/yesummit/global/auth/config/WebMvcConfig.java b/src/main/java/univ/yesummit/global/auth/config/WebMvcConfig.java index a7c675c..e132a2b 100644 --- a/src/main/java/univ/yesummit/global/auth/config/WebMvcConfig.java +++ b/src/main/java/univ/yesummit/global/auth/config/WebMvcConfig.java @@ -26,5 +26,9 @@ public void addCorsMappings(final CorsRegistry registry ){ .allowCredentials(true); } - + @Override + public void addArgumentResolvers(List resolvers) { + WebMvcConfigurer.super.addArgumentResolvers(resolvers); // 기존 Resolver + resolvers.add(authArgumentResolver); // 커스텀 Resolver + } } \ No newline at end of file From 9983e86144cfc2ad0ac70abf03e9a7cc41bb68fe Mon Sep 17 00:00:00 2001 From: sonsumin Date: Tue, 19 Nov 2024 22:58:38 +0900 Subject: [PATCH 07/73] =?UTF-8?q?[#2]=E2=9C=A8Feat:=20JWT=20=ED=86=A0?= =?UTF-8?q?=ED=81=B0=20=EC=83=9D=EC=84=B1=20Utils?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yesummit/global/auth/util/JwtUtils.java | 105 ++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 src/main/java/univ/yesummit/global/auth/util/JwtUtils.java diff --git a/src/main/java/univ/yesummit/global/auth/util/JwtUtils.java b/src/main/java/univ/yesummit/global/auth/util/JwtUtils.java new file mode 100644 index 0000000..4d2b803 --- /dev/null +++ b/src/main/java/univ/yesummit/global/auth/util/JwtUtils.java @@ -0,0 +1,105 @@ +package univ.yesummit.global.auth.util; + +import com.auth0.jwt.JWT; +import com.auth0.jwt.algorithms.Algorithm; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import univ.yesummit.domain.member.repository.MemberRepository; + +import java.util.Date; +import java.util.Optional; + +@Getter +@Service +@RequiredArgsConstructor +@Slf4j +public class JwtUtils { + + @Value("${jwt.secret}") + private String secret; + + @Value("${jwt.access.expiration}") + private Long accessExpiration; + + @Value("${jwt.refresh.expiration}") + private Long refreshExpiration; + + @Value("${jwt.access.header}") + private String accessHeader; + + @Value("${jwt.refresh.header}") + private String refreshHeader; + + private static final String ACCESS_TOKEN_SUBJECT = "AccessToken"; + private static final String REFRESH_TOKEN_SUBJECT = "RefreshToken"; + private static final String ID_CLAIM = "member_id"; + private static final String BEARER = "Bearer "; + + private final MemberRepository memberRepository; + + public String createAccessToken(Long memberId) { + + log.info("memberId info: {}", memberId.toString()); + + String accessToken = JWT.create() + .withSubject(ACCESS_TOKEN_SUBJECT) + .withExpiresAt(new Date(System.currentTimeMillis() + accessExpiration)) + .withClaim(ID_CLAIM, memberId) + .sign(Algorithm.HMAC512(secret)); + + + log.info("Generated Access Token: {}", accessToken); + + return accessToken; + } + + + public String createRefreshToken() { + return JWT.create() + .withSubject(REFRESH_TOKEN_SUBJECT) + .withExpiresAt(new Date(System.currentTimeMillis() + refreshExpiration)) + .sign(Algorithm.HMAC512(secret)); + } + + public void sendAccessAndRefreshToken(HttpServletResponse response, String accessToken, String refreshToken) { + response.setStatus(HttpServletResponse.SC_OK); + + response.setHeader(accessHeader, accessToken); + response.setHeader(refreshHeader, refreshToken); + log.info("Access Token: {}, Refresh Token: {}", accessToken, refreshToken); + } + + public Optional extractAccessToken(HttpServletRequest request) { + String header = request.getHeader(accessHeader); + log.info("Access header: {}", header); + return Optional.ofNullable(request.getHeader(accessHeader)) + .filter(accessToken -> accessToken.startsWith(BEARER)) + .map(accessToken -> accessToken.replace(BEARER, "")); + } + + public Optional extractMemberId(String token) { + Long memberId = JWT.require(Algorithm.HMAC512(secret)) + .build() + .verify(token) + .getClaim("member_id") + .asLong(); + + return Optional.ofNullable(memberId); + } + /* 토큰 유효성 검증 */ + public boolean isValid(String token) { + try { + JWT.require(Algorithm.HMAC512(secret)).build().verify(token); + return true; + } catch (Exception e) { + log.error("유효하지 않은 Token입니다", e.getMessage()); + return false; + } + } + //==========================================================================// +} \ No newline at end of file From d8e3d4c0dcbda494ea4014046c57bfee5073d909 Mon Sep 17 00:00:00 2001 From: sonsumin Date: Tue, 19 Nov 2024 22:59:25 +0900 Subject: [PATCH 08/73] =?UTF-8?q?[#2]=E2=9C=A8Feat:=20=EB=A6=AC=ED=94=84?= =?UTF-8?q?=EB=A0=88=EC=8B=9C=20=ED=86=A0=ED=81=B0=20=EC=82=AD=EC=A0=9C=20?= =?UTF-8?q?=EB=B0=8F=20=EC=B6=94=EA=B0=80=20=EC=BD=94=EB=93=9C=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yesummit/global/auth/util/JwtUtils.java | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/src/main/java/univ/yesummit/global/auth/util/JwtUtils.java b/src/main/java/univ/yesummit/global/auth/util/JwtUtils.java index 4d2b803..27238ee 100644 --- a/src/main/java/univ/yesummit/global/auth/util/JwtUtils.java +++ b/src/main/java/univ/yesummit/global/auth/util/JwtUtils.java @@ -2,8 +2,12 @@ import com.auth0.jwt.JWT; import com.auth0.jwt.algorithms.Algorithm; +import com.auth0.jwt.interfaces.DecodedJWT; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import jakarta.transaction.Transactional; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -12,6 +16,8 @@ import univ.yesummit.domain.member.repository.MemberRepository; import java.util.Date; +import java.util.HashMap; +import java.util.Map; import java.util.Optional; @Getter @@ -91,6 +97,58 @@ public Optional extractMemberId(String token) { return Optional.ofNullable(memberId); } + +// public Map getClaims(String token) { +// try { +// DecodedJWT decodedJWT = JWT.require(Algorithm.HMAC512(secret)) // 동일한 키와 알고리즘 사용 +// .build() +// .verify(token); // 토큰 검증 +// +// // 클레임 반환 +// Map claims = new HashMap<>(); +// claims.put("subject", decodedJWT.getSubject()); +// claims.put("member_id", decodedJWT.getClaim(ID_CLAIM).asLong()); +// claims.put("expiresAt", decodedJWT.getExpiresAt()); +// +// return claims; +// } catch (Exception e) { +// log.error("Invalid token: {}", e.getMessage()); +// throw new IllegalArgumentException("Invalid token", e); +// } +// } + + //== 추후에 유지보수하는 과정에서 리프레시 토큰의 만료 혹은 토큰의 블랙리스트를 구현할 때 사용할 것 ==// + +// +// public void destroyRefreshToken(String username) { +// memberRepository.findByUsername(username) +// .ifPresentOrElse( +// Member::destroyRefreshToken, +// () -> { throw new MemberException(ErrorCode.NOT_FOUND_MEMBER); } +// ); +// } +// +// public void updateRefreshToken(String username, String refreshToken) { +// memberRepository.findByUsername(username) +// .ifPresentOrElse( +// member -> member.updateRefreshToken(refreshToken), +// () -> { throw new MemberException(ErrorCode.NOT_FOUND_MEMBER); } +// ); +// } + + public Optional extractRefreshToken(HttpServletRequest request) { + return Optional.ofNullable(request.getHeader(refreshHeader)) + .filter(refreshToken -> refreshToken.startsWith(BEARER)) + .map(refreshToken -> refreshToken.replace(BEARER, "")); + } + + public void sendAccessToken(HttpServletResponse response, String accessToken) { + response.setStatus(HttpServletResponse.SC_OK); + + response.setHeader(accessHeader, accessToken); + log.info("Send AccessToken: {}", accessToken); + } + /* 토큰 유효성 검증 */ public boolean isValid(String token) { try { From 2679842cf2e2e15ffef98ea00db5b7b514f155fd Mon Sep 17 00:00:00 2001 From: sonsumin Date: Tue, 19 Nov 2024 23:00:11 +0900 Subject: [PATCH 09/73] =?UTF-8?q?[#2]=E2=9C=A8Feat:=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=9D=B8=20=EC=9D=B8=EC=A6=9D=20=EC=9D=B8=EA=B0=80=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/auth/util/SecurityUtil.java | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 src/main/java/univ/yesummit/global/auth/util/SecurityUtil.java diff --git a/src/main/java/univ/yesummit/global/auth/util/SecurityUtil.java b/src/main/java/univ/yesummit/global/auth/util/SecurityUtil.java new file mode 100644 index 0000000..60d3bf5 --- /dev/null +++ b/src/main/java/univ/yesummit/global/auth/util/SecurityUtil.java @@ -0,0 +1,27 @@ +package univ.yesummit.global.auth.util; + +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.oauth2.core.user.OAuth2User; + +public class SecurityUtil { + + public static String getLoginUsername() { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if (authentication == null || !authentication.isAuthenticated()) { + throw new IllegalStateException("User is not authenticated"); + } + + Object principal = authentication.getPrincipal(); + if (principal instanceof UserDetails) { + return ((UserDetails) principal).getUsername(); + } else if (principal instanceof OAuth2User) { + return ((OAuth2User) principal).getAttribute("name"); // 필요에 따라 변경 가능 + } else if (principal instanceof String) { + return (String) principal; + } + + throw new IllegalStateException("Unknown principal type"); + } +} \ No newline at end of file From 2d403ba1f502194411db6cb8e8167d9dfcf11d42 Mon Sep 17 00:00:00 2001 From: sonsumin Date: Tue, 19 Nov 2024 23:01:09 +0900 Subject: [PATCH 10/73] =?UTF-8?q?[#3]=E2=9C=A8Feat:=20=EC=86=8C=EC=85=9C?= =?UTF-8?q?=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EC=97=94=EB=93=9C=ED=8F=AC?= =?UTF-8?q?=EC=9D=B8=ED=8A=B8=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/auth/controller/AuthController.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 src/main/java/univ/yesummit/global/auth/controller/AuthController.java diff --git a/src/main/java/univ/yesummit/global/auth/controller/AuthController.java b/src/main/java/univ/yesummit/global/auth/controller/AuthController.java new file mode 100644 index 0000000..5fafdd2 --- /dev/null +++ b/src/main/java/univ/yesummit/global/auth/controller/AuthController.java @@ -0,0 +1,16 @@ +package univ.yesummit.global.auth.controller; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.servlet.view.RedirectView; + +@RestController +@RequestMapping("/v1/api/kakao") +public class AuthController { + + @GetMapping("/login") + public RedirectView login() { + return new RedirectView("/oauth2/authorization/kakao"); + } +} \ No newline at end of file From a97aa16bc99dd40c1fd3571cdf4f2cbf5e9b916c Mon Sep 17 00:00:00 2001 From: sonsumin Date: Tue, 19 Nov 2024 23:04:27 +0900 Subject: [PATCH 11/73] =?UTF-8?q?[#4]=E2=9C=A8Feat:=20=EC=8A=A4=EC=9B=A8?= =?UTF-8?q?=EA=B1=B0=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/swagger/SwaggerConfig.java | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 src/main/java/univ/yesummit/global/swagger/SwaggerConfig.java diff --git a/src/main/java/univ/yesummit/global/swagger/SwaggerConfig.java b/src/main/java/univ/yesummit/global/swagger/SwaggerConfig.java new file mode 100644 index 0000000..537d890 --- /dev/null +++ b/src/main/java/univ/yesummit/global/swagger/SwaggerConfig.java @@ -0,0 +1,21 @@ +package univ.yesummit.global.swagger; + +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.info.Info; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + + +@Configuration +public class SwaggerConfig { + + @Bean + public OpenAPI openAPI() { + return new OpenAPI() + .info(new Info() + .version("v1.0.0") // 버전 기록 + .title("YE;Summit API 명세서") // API 명세서 제목 + .description("API 명세서")); // 상세명세서 + + } +} \ No newline at end of file From 39941e2f29dc7075b5af9a5c38a5a2b712dcd3f9 Mon Sep 17 00:00:00 2001 From: sonsumin Date: Tue, 19 Nov 2024 23:10:32 +0900 Subject: [PATCH 12/73] =?UTF-8?q?[#2]=E2=9C=A8Feat:=20OAuth2MemberService?= =?UTF-8?q?=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/oauth/OAuth2MemberService.java | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 src/main/java/univ/yesummit/global/oauth/OAuth2MemberService.java diff --git a/src/main/java/univ/yesummit/global/oauth/OAuth2MemberService.java b/src/main/java/univ/yesummit/global/oauth/OAuth2MemberService.java new file mode 100644 index 0000000..0fa998c --- /dev/null +++ b/src/main/java/univ/yesummit/global/oauth/OAuth2MemberService.java @@ -0,0 +1,69 @@ +package univ.yesummit.global.oauth; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService; +import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest; +import org.springframework.security.oauth2.core.OAuth2AuthenticationException; +import org.springframework.security.oauth2.core.user.OAuth2User; +import org.springframework.stereotype.Service; +import univ.yesummit.domain.member.entity.Member; +import univ.yesummit.domain.member.repository.MemberRepository; +import univ.yesummit.global.oauth.dto.KakaoResponse; +import univ.yesummit.global.oauth.dto.OAuth2Response; + +import java.util.Optional; + +@Service +@RequiredArgsConstructor +@Slf4j +public class OAuth2MemberService extends DefaultOAuth2UserService { //DefaultOAuth2UserService를 상속받아 기본 OAuth2 사용자 정보를 로드하는 기능을 확장 + + private final MemberRepository memberRepository; + + @Override + public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException { + OAuth2User oAuth2User = super.loadUser(userRequest); //OAuth2 서버에서 사용자 정보를 가져옴 + log.info("memberInfo = {}", oAuth2User.getAttributes()); + + OAuth2Response oAuth2Response = getOAuth2Response(userRequest, oAuth2User) + .orElseThrow(() -> new OAuth2AuthenticationException( + "Unsupported provider: " + userRequest.getClientRegistration().getRegistrationId())); + + Member member = getOrSave(oAuth2Response); + + return OAuth2Member.from(member); + } + + // 소셜 로그인 제공자(Kakao, Google) 구분 + private Optional getOAuth2Response(OAuth2UserRequest userRequest, OAuth2User oAuth2User) { + + String registrationId = userRequest.getClientRegistration().getRegistrationId(); + + OAuth2Response oAuth2Response; + + if ("kakao".equals(registrationId)) { + oAuth2Response = new KakaoResponse(oAuth2User.getAttributes()); + } else { + throw new OAuth2AuthenticationException("Unsupported provider: " + registrationId); + } + + return Optional.of(oAuth2Response); + } + + /** + * provider와 providerId를 기준으로 데이터베이스에서 사용자를 조회 + * @param oAuth2Response + * @return + */ + private Member getOrSave(OAuth2Response oAuth2Response) { + Optional optionalMember = memberRepository.findByProviderAndProviderId(oAuth2Response.getProvider(), oAuth2Response.getProviderId()); + + if (optionalMember.isPresent()) { + return optionalMember.get(); + } else { + Member member = oAuth2Response.toEntity(); + return memberRepository.save(member); + } + } +} \ No newline at end of file From 44b55869549b26f0d5ad17119cb7fc28bbe918b8 Mon Sep 17 00:00:00 2001 From: sonsumin Date: Tue, 19 Nov 2024 23:10:43 +0900 Subject: [PATCH 13/73] =?UTF-8?q?[#2]=E2=9C=A8Feat:=20OAuth2Response=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/oauth/dto/OAuth2Response.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 src/main/java/univ/yesummit/global/oauth/dto/OAuth2Response.java diff --git a/src/main/java/univ/yesummit/global/oauth/dto/OAuth2Response.java b/src/main/java/univ/yesummit/global/oauth/dto/OAuth2Response.java new file mode 100644 index 0000000..57962af --- /dev/null +++ b/src/main/java/univ/yesummit/global/oauth/dto/OAuth2Response.java @@ -0,0 +1,19 @@ +package univ.yesummit.global.oauth.dto; + +import univ.yesummit.domain.member.entity.Member; + +public interface OAuth2Response { + + // 제공자 (naver, google, kakao 등) + String getProvider(); + + // 제공자에서 발급해주는 아이디 + String getProviderId(); + + // 사용자 정보 + String getEmail(); + + String getName(); + + Member toEntity(); +} \ No newline at end of file From df8426d3cb8a0f8ce5d906a52b0dba8f4c4d5147 Mon Sep 17 00:00:00 2001 From: sonsumin Date: Tue, 19 Nov 2024 23:11:04 +0900 Subject: [PATCH 14/73] =?UTF-8?q?[#2]=E2=9C=A8Feat:=20=EC=86=8C=EC=85=9C?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=9D=B8=20Success=20Handler=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/oauth/OAuth2SuccessHandler.java | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 src/main/java/univ/yesummit/global/oauth/OAuth2SuccessHandler.java diff --git a/src/main/java/univ/yesummit/global/oauth/OAuth2SuccessHandler.java b/src/main/java/univ/yesummit/global/oauth/OAuth2SuccessHandler.java new file mode 100644 index 0000000..d411074 --- /dev/null +++ b/src/main/java/univ/yesummit/global/oauth/OAuth2SuccessHandler.java @@ -0,0 +1,47 @@ +package univ.yesummit.global.oauth; + +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.core.Authentication; +import org.springframework.security.web.authentication.AuthenticationSuccessHandler; +import org.springframework.stereotype.Component; +import univ.yesummit.domain.member.service.MemberService; +import univ.yesummit.global.auth.util.JwtUtils; + +import java.io.IOException; + +@Slf4j +@Component +@RequiredArgsConstructor +public class OAuth2SuccessHandler implements AuthenticationSuccessHandler { + + private final JwtUtils jwtUtils; + private final MemberService memberService; + + @Override + public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { + log.info("OAuth2SuccessHandler.onAuthenticationSuccess Member Name : {}", authentication.getName()); + + OAuth2Member oAuth2Member = (OAuth2Member) authentication.getPrincipal(); + Long memberId = oAuth2Member.getMemberId(); + + // JWT 토큰 발급 + String accessToken = jwtUtils.createAccessToken(memberId); + String refreshToken = jwtUtils.createRefreshToken(); + jwtUtils.sendAccessAndRefreshToken(response, accessToken, refreshToken); + + // 기존 회원인지 확인 + // 기존 회원인지 확인 + if (memberService.isFirstLogin(memberId)) { + // 첫 로그인 시 추가 정보 입력 페이지로 리다이렉트 + response.sendRedirect("/additional-info"); + return; + } + + // 기존 회원이라면 정상 응답 (e.g., 메인 페이지로 리다이렉트) + response.sendRedirect("/home"); + } +} From 5fa4aec6dbd89bdb3c70e09b32051ddea3ff5268 Mon Sep 17 00:00:00 2001 From: sonsumin Date: Tue, 19 Nov 2024 23:11:31 +0900 Subject: [PATCH 15/73] =?UTF-8?q?[#3]=E2=9C=A8Feat:=20=EC=86=8C=EC=85=9C?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EB=B0=98=ED=99=98=EA=B0=92=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/oauth/dto/KakaoResponse.java | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 src/main/java/univ/yesummit/global/oauth/dto/KakaoResponse.java diff --git a/src/main/java/univ/yesummit/global/oauth/dto/KakaoResponse.java b/src/main/java/univ/yesummit/global/oauth/dto/KakaoResponse.java new file mode 100644 index 0000000..84b3647 --- /dev/null +++ b/src/main/java/univ/yesummit/global/oauth/dto/KakaoResponse.java @@ -0,0 +1,68 @@ +package univ.yesummit.global.oauth.dto; + +import lombok.RequiredArgsConstructor; +import univ.yesummit.domain.member.entity.Authority; +import univ.yesummit.domain.member.entity.Member; + +import java.util.Map; + +@RequiredArgsConstructor +public class KakaoResponse implements OAuth2Response { + + private final Map attributes; + + /** + * 반환값 "kakao" + * 인증 제공자를 식별, 이 값은 추후 멀티 OAuth2 제공자(Google, Naver 등) + * 을 구현할 때 사용됨 + */ + @Override + public String getProvider() { + return "kakao"; + } + + /** + * Kakao 사용자 고유 ID 반환 + * attributes 맵에서 "id" 키에 해당하는 값을 가져옴 + * KakaoResponse는 이 고유 ID를 providerId로 사용 + * 이 값은 해당 사용자를 Kakao 서비스 내에서 유일하게 식별 + */ + @Override + public String getProviderId() { + return attributes.get("id").toString(); + } + + /** + * attributes에서 "kakao_account"라는 키로 사용자 계정 정보를 가져옴 + * 이 값은 다시 Map 형태로 "email"키로 이메일을 추출 + * Kakao 계정 설정에서 이메일 제공이 허용되지 않으면 null이 반환 + */ + @Override + public String getEmail() { + return (String) ((Map) attributes.get("kakao_account")).get("email"); + } + + /** + * attributes에서 "properties" 키로 사용자 프로필 정보를 가져옴 + * Map 형태로 "nickname" 키를 통해 닉네임을 추출 + */ + @Override + public String getName() { + return (String) ((Map) attributes.get("properties")).get("nickname"); + } + + /** + * 소셜 로그인을 통해 받은 사용자 정보를 기반으로 애플리케이션의 Member 엔티티를 생성 + * 이후 이 엔티티는 데이터베이스에 저장 + */ + @Override + public Member toEntity() { + return Member.builder() + .provider(getProvider()) + .providerId(getProviderId()) + .email(getEmail()) + .username(getName()) + .authority(Authority.USER) + .build(); + } +} From 29299aae66bb889ca5976f25b962d681cec5a9a3 Mon Sep 17 00:00:00 2001 From: sonsumin Date: Tue, 19 Nov 2024 23:12:32 +0900 Subject: [PATCH 16/73] =?UTF-8?q?[#3]=E2=9C=A8Feat:=20OAuth2Member=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yesummit/global/oauth/OAuth2Member.java | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 src/main/java/univ/yesummit/global/oauth/OAuth2Member.java diff --git a/src/main/java/univ/yesummit/global/oauth/OAuth2Member.java b/src/main/java/univ/yesummit/global/oauth/OAuth2Member.java new file mode 100644 index 0000000..e9d8f12 --- /dev/null +++ b/src/main/java/univ/yesummit/global/oauth/OAuth2Member.java @@ -0,0 +1,37 @@ +package univ.yesummit.global.oauth; + +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.oauth2.core.user.OAuth2User; +import univ.yesummit.domain.member.entity.Authority; +import univ.yesummit.domain.member.entity.Member; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; + +public record OAuth2Member(Member member) implements OAuth2User { + + @Override + public Map getAttributes() { + return Map.of(); + } + + @Override + public Collection getAuthorities() { + return Collections.singletonList(new SimpleGrantedAuthority(Authority.USER.name())); + } + + @Override + public String getName() { + return member.getUsername(); + } + + public Long getMemberId() { + return member.getId(); + } + + public static OAuth2Member from(Member member) { + return new OAuth2Member(member); + } +} From ae43d15f3a461e9dd2e2644aeb8889af6e840d98 Mon Sep 17 00:00:00 2001 From: sonsumin Date: Tue, 19 Nov 2024 23:15:54 +0900 Subject: [PATCH 17/73] =?UTF-8?q?=F0=9F=99=88Feat:=20yml=ED=8C=8C=EC=9D=BC?= =?UTF-8?q?=20ignore=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index c2065bc..2c78df7 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,8 @@ build/ !gradle/wrapper/gradle-wrapper.jar !**/src/main/**/build/ !**/src/test/**/build/ +application.yml +application-*.yml ### STS ### .apt_generated From 35c763b13f9d5b39a9f99c31a76837e5d384b755 Mon Sep 17 00:00:00 2001 From: sonsumin Date: Wed, 20 Nov 2024 13:03:36 +0900 Subject: [PATCH 18/73] =?UTF-8?q?[#3]=E2=9C=A8Feat:=20MemberSignUpDTO=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/member/dto/MemberSignUpDTO.java | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 src/main/java/univ/yesummit/domain/member/dto/MemberSignUpDTO.java diff --git a/src/main/java/univ/yesummit/domain/member/dto/MemberSignUpDTO.java b/src/main/java/univ/yesummit/domain/member/dto/MemberSignUpDTO.java new file mode 100644 index 0000000..fa848df --- /dev/null +++ b/src/main/java/univ/yesummit/domain/member/dto/MemberSignUpDTO.java @@ -0,0 +1,58 @@ +package univ.yesummit.domain.member.dto; + + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.Size; +import univ.yesummit.domain.member.entity.Member; +import univ.yesummit.domain.member.entity.UserType; + +public record MemberSignUpDTO( + + @NotBlank(message = "이름을 입력해주세요") + @Size(min = 2, message = "사용자 이름이 너무 짧습니다.") + @Pattern(regexp = "^[A-Za-z가-힣]+$", message = "사용자 이름은 한글 또는 알파벳만 입력해주세요.") + String name, + + @NotBlank(message = "연락처를 입력해주세요") + String phoneNumber, + + @NotBlank(message = "이메일을 입력해주세요") + String email, + + @NotNull(message = "참여자 구분을 선택해주세요") + UserType userType, // 청년 창업가 or 예비 투자자 + + @NotBlank(message = "소속 회사를 입력해주세요") + String company, + + @NotBlank(message = "사내 직책을 입력해주세요") + String position, + + String businessRegistrationNumber, // 사업자 등록 번호 (선택적) + + String businessIdeaField, // 사업 아이디어 및 분야 (선택적) + + @NotNull(message = "써밋 알림톡 수신 여부를 선택해주세요") + Boolean consentSummitAlerts, // 써밋 알림톡 수신 여부 + + @NotNull(message = "개인정보 수집 및 이용 동의를 선택해주세요") + Boolean consentPrivacyPolicy // 개인정보 수집 및 이용 동의 +) +{ + public Member toEntity() { + return Member.builder() + .username(name) + .phoneNumber(phoneNumber) + .email(email) + .userType(userType) + .company(company) + .position(position) + .businessRegistrationNumber(businessRegistrationNumber) + .businessIdeaField(businessIdeaField) + .consentSummitAlerts(consentSummitAlerts) + .consentPrivacyPolicy(consentPrivacyPolicy) + .build(); + } +} \ No newline at end of file From 1e10dd29f7ec5a1e4bcfb0321ba151ccbdc945ab Mon Sep 17 00:00:00 2001 From: sonsumin Date: Wed, 20 Nov 2024 13:03:46 +0900 Subject: [PATCH 19/73] =?UTF-8?q?[#3]=E2=9C=A8Feat:=20MemberWithdrawDTO=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/member/dto/MemberWithdrawDTO.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/main/java/univ/yesummit/domain/member/dto/MemberWithdrawDTO.java diff --git a/src/main/java/univ/yesummit/domain/member/dto/MemberWithdrawDTO.java b/src/main/java/univ/yesummit/domain/member/dto/MemberWithdrawDTO.java new file mode 100644 index 0000000..179f342 --- /dev/null +++ b/src/main/java/univ/yesummit/domain/member/dto/MemberWithdrawDTO.java @@ -0,0 +1,12 @@ +package univ.yesummit.domain.member.dto; + +import jakarta.validation.constraints.NotBlank; + +public record MemberWithdrawDTO( + @NotBlank(message = "이름을 입력해주세요") + String username, + + @NotBlank(message = "이메일을 입력해주세요") + String email +) { +} From cc18ee6e69cc39f5ce50902ee1491889199d0d2d Mon Sep 17 00:00:00 2001 From: sonsumin Date: Wed, 20 Nov 2024 13:03:54 +0900 Subject: [PATCH 20/73] =?UTF-8?q?[#3]=E2=9C=A8Feat:=20MemberUpdateDTO=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../univ/yesummit/domain/member/dto/MemberUpdateDTO.java | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/main/java/univ/yesummit/domain/member/dto/MemberUpdateDTO.java diff --git a/src/main/java/univ/yesummit/domain/member/dto/MemberUpdateDTO.java b/src/main/java/univ/yesummit/domain/member/dto/MemberUpdateDTO.java new file mode 100644 index 0000000..05f8d33 --- /dev/null +++ b/src/main/java/univ/yesummit/domain/member/dto/MemberUpdateDTO.java @@ -0,0 +1,9 @@ +package univ.yesummit.domain.member.dto; + +import java.util.Optional; + +public record MemberUpdateDTO( + Optional phoneNumber, + Optional businessIdeaField, + Optional consentSummitAlerts +) {} From c661cbd1c25af465aa5fac7689525b48154455cf Mon Sep 17 00:00:00 2001 From: sonsumin Date: Wed, 20 Nov 2024 13:04:12 +0900 Subject: [PATCH 21/73] =?UTF-8?q?[#3]=E2=9C=A8Feat:=20MemberService=20?= =?UTF-8?q?=EC=9D=B8=ED=84=B0=ED=8E=98=EC=9D=B4=EC=8A=A4=20=EB=B0=8F=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=EC=B2=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/member/service/MemberService.java | 36 ++++++++ .../service/impl/MemberServiceImpl.java | 86 +++++++++++++++++++ 2 files changed, 122 insertions(+) create mode 100644 src/main/java/univ/yesummit/domain/member/service/MemberService.java create mode 100644 src/main/java/univ/yesummit/domain/member/service/impl/MemberServiceImpl.java diff --git a/src/main/java/univ/yesummit/domain/member/service/MemberService.java b/src/main/java/univ/yesummit/domain/member/service/MemberService.java new file mode 100644 index 0000000..0100d26 --- /dev/null +++ b/src/main/java/univ/yesummit/domain/member/service/MemberService.java @@ -0,0 +1,36 @@ +package univ.yesummit.domain.member.service; + + +import univ.yesummit.domain.member.dto.MemberInfoDTO; +import univ.yesummit.domain.member.dto.MemberSignUpDTO; +import univ.yesummit.domain.member.dto.MemberUpdateDTO; +import univ.yesummit.domain.member.dto.MemberWithdrawDTO; + +public interface MemberService { + + /** + * 추가 정보 수집 + */ + void saveAdditionalInfo(MemberSignUpDTO dto, Long memberId) throws Exception; + + /** + * 첫 로그인 확인 + */ + boolean isFirstLogin(Long memberId); + + /** + * 회원 탈퇴 + */ + void withdraw(Long memberId, MemberWithdrawDTO dto) throws Exception; + + /** + * 회원 정보 수정 + */ + void updateInfo(MemberUpdateDTO dto) throws Exception; + + /** + * 내 정보 조회 + */ + MemberInfoDTO getMyInfo(Long memberId) throws Exception; + +} diff --git a/src/main/java/univ/yesummit/domain/member/service/impl/MemberServiceImpl.java b/src/main/java/univ/yesummit/domain/member/service/impl/MemberServiceImpl.java new file mode 100644 index 0000000..92fa9b6 --- /dev/null +++ b/src/main/java/univ/yesummit/domain/member/service/impl/MemberServiceImpl.java @@ -0,0 +1,86 @@ +package univ.yesummit.domain.member.service.impl; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import univ.yesummit.domain.member.dto.MemberInfoDTO; +import univ.yesummit.domain.member.dto.MemberSignUpDTO; +import univ.yesummit.domain.member.dto.MemberUpdateDTO; +import univ.yesummit.domain.member.dto.MemberWithdrawDTO; +import univ.yesummit.domain.member.entity.Member; +import univ.yesummit.domain.member.exception.MemberException; +import univ.yesummit.domain.member.repository.MemberRepository; +import univ.yesummit.domain.member.service.MemberService; +import univ.yesummit.global.auth.util.SecurityUtil; +import univ.yesummit.global.exception.ErrorCode; + +@Service +@RequiredArgsConstructor +@Slf4j +public class MemberServiceImpl implements MemberService { + + private final MemberRepository memberRepository; + + @Override + public void saveAdditionalInfo(MemberSignUpDTO memberSignUpDTO, Long memberId) throws Exception { + + if (memberId == null) { + throw new IllegalArgumentException("회원 ID가 null입니다. 인증 정보를 확인하세요."); + } + // 회원이 존재하지 않으면 예외를 발생 + Member member = memberRepository.findById(memberId) + .orElseThrow(() -> new MemberException(ErrorCode.NOT_FOUND_MEMBER)); + + // 추가 정보 업데이트 + member.updateAdditionalInfo( + memberSignUpDTO.phoneNumber(), + memberSignUpDTO.userType(), + memberSignUpDTO.company(), + memberSignUpDTO.position(), + memberSignUpDTO.businessRegistrationNumber(), + memberSignUpDTO.businessIdeaField(), + memberSignUpDTO.consentSummitAlerts(), + memberSignUpDTO.consentPrivacyPolicy() + ); + + member.addUserAuthority(); // 권한 추가 + memberRepository.save(member); // 변경된 정보 저장 + } + + // 첫 로그인 여부 확인 + @Override + public boolean isFirstLogin(Long memberId) { + return memberRepository.findById(memberId) + .map(member -> member.getPhoneNumber() == null || member.getUserType() == null) + .orElse(false); + } + + @Override + public void withdraw(Long memberId, MemberWithdrawDTO memberWithdrawDTO) throws Exception { + Member member = memberRepository.findById(memberId) + .orElseThrow(() -> new IllegalArgumentException("입력된 정보와 일치하는 사용자가 없습니다.")); + + if (!member.getEmail().equals(memberWithdrawDTO.email())) { + throw new MemberException(ErrorCode.MISMATCH_EMAIL); + } + + memberRepository.delete(member); // 데이터베이스에서 완전히 삭제 + } + + @Override + public void updateInfo(MemberUpdateDTO memberUpdateDTO) throws Exception { + + Member member = memberRepository.getByUsernameOrThrow(SecurityUtil.getLoginUsername()); + + memberUpdateDTO.phoneNumber().ifPresent(member::updatePhoneNumber); + memberUpdateDTO.businessIdeaField().ifPresent(member::updateBusinessIdeaField); + memberUpdateDTO.consentSummitAlerts().ifPresent(member::updateConsentSummitAlerts); + } + + @Override + public MemberInfoDTO getMyInfo(Long memberId) throws Exception { + Member member = memberRepository.findById(memberId) + .orElseThrow(() -> new MemberException(ErrorCode.NOT_FOUND_MEMBER)); + return new MemberInfoDTO(member); + } +} From dc3d471e1f5252a05bca28bb7002b2ef0ed4a445 Mon Sep 17 00:00:00 2001 From: sonsumin Date: Wed, 20 Nov 2024 13:05:06 +0900 Subject: [PATCH 22/73] =?UTF-8?q?[#3]=E2=9C=A8Feat:=20MemberRepository=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/repository/MemberRepository.java | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 src/main/java/univ/yesummit/domain/member/repository/MemberRepository.java diff --git a/src/main/java/univ/yesummit/domain/member/repository/MemberRepository.java b/src/main/java/univ/yesummit/domain/member/repository/MemberRepository.java new file mode 100644 index 0000000..d31d7f4 --- /dev/null +++ b/src/main/java/univ/yesummit/domain/member/repository/MemberRepository.java @@ -0,0 +1,26 @@ +package univ.yesummit.domain.member.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import univ.yesummit.domain.member.entity.Member; +import univ.yesummit.domain.member.exception.MemberException; +import univ.yesummit.global.exception.ErrorCode; + +import java.util.Optional; + +public interface MemberRepository extends JpaRepository { + + default Member getByUsernameOrThrow(String username) { + return findByUsername(username) + .orElseThrow(() -> new MemberException(ErrorCode.NOT_FOUND_MEMBER)); + } + + Optional findByUsername(String username); + + Optional findByUsernameAndEmail(String username, String email); + + Optional findByEmail(String email); + + Optional findByProviderAndProviderId(String provider, String providerId); + + boolean existsByUsername(String username); +} From 16f91fbe1ddf62faf7c56e91b088536cf9ae66be Mon Sep 17 00:00:00 2001 From: sonsumin Date: Wed, 20 Nov 2024 13:05:36 +0900 Subject: [PATCH 23/73] =?UTF-8?q?[#2]=E2=9C=A8Feat:=20AuthArgumentResolver?= =?UTF-8?q?=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/resolver/AuthArgumentResolver.java | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 src/main/java/univ/yesummit/global/resolver/AuthArgumentResolver.java diff --git a/src/main/java/univ/yesummit/global/resolver/AuthArgumentResolver.java b/src/main/java/univ/yesummit/global/resolver/AuthArgumentResolver.java new file mode 100644 index 0000000..b7bb9eb --- /dev/null +++ b/src/main/java/univ/yesummit/global/resolver/AuthArgumentResolver.java @@ -0,0 +1,62 @@ +package univ.yesummit.global.resolver; + +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import org.springframework.core.MethodParameter; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.support.WebDataBinderFactory; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.method.support.ModelAndViewContainer; +import univ.yesummit.domain.member.exception.MemberException; +import univ.yesummit.global.auth.util.JwtUtils; +import univ.yesummit.global.exception.ErrorCode; + +import java.util.Objects; + +/** + * 컨트롤러 메서드의 파라미터에 어노테이션 @User , LoginUser가 사용된 경우 + * 요청의 JWT 토큰에서 사용자 ID를 추출하여 해당 파라미터에 주입 + */ +@Component +@RequiredArgsConstructor +public class AuthArgumentResolver implements HandlerMethodArgumentResolver { + + private final JwtUtils jwtUtils; + + /** + * 현재 파라미터가 여기 resolver에서 처리할 대상인지 확인 + * @param parameter + * @return + */ + @Override + public boolean supportsParameter(MethodParameter parameter) { + return parameter.hasParameterAnnotation(User.class) + && parameter.getParameterType().equals(LoginUser.class); + } + + /** + * 요청에서 JWT토큰을 추출하고 해당 토큰에서 사용자 ID를 가져와 LoginUser 객체를 생성 -> 반환 + * @param parameter + * @param mavContainer + * @param webRequest + * @param binderFactory + * @return + */ + @Override + public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, + NativeWebRequest webRequest, WebDataBinderFactory binderFactory) { + HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class); + + // Access Token 추출 + String accessToken = jwtUtils.extractAccessToken(Objects.requireNonNull(request)) + .orElseThrow(() -> new MemberException(ErrorCode.NOT_FOUND_ACCESS_TOKEN)); + + // memberId 추출 + Long memberId = jwtUtils.extractMemberId(accessToken) + .orElseThrow(() -> new MemberException(ErrorCode.INVALID_ACCESS_TOKEN)); + + // LoginUser 객체 반환 + return new LoginUser(memberId); + } +} From 85a727efc24f8d1b66b111809f0f27123d19ebf5 Mon Sep 17 00:00:00 2001 From: sonsumin Date: Wed, 20 Nov 2024 13:06:03 +0900 Subject: [PATCH 24/73] =?UTF-8?q?[#3]=E2=9C=A8Feat:=20member-Authority=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/univ/yesummit/domain/member/entity/Authority.java | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 src/main/java/univ/yesummit/domain/member/entity/Authority.java diff --git a/src/main/java/univ/yesummit/domain/member/entity/Authority.java b/src/main/java/univ/yesummit/domain/member/entity/Authority.java new file mode 100644 index 0000000..8526da1 --- /dev/null +++ b/src/main/java/univ/yesummit/domain/member/entity/Authority.java @@ -0,0 +1,5 @@ +package univ.yesummit.domain.member.entity; + +public enum Authority { + ADMIN, USER, SIGN_OUT +} From 41d53a90485b5e449b56247be17904eaa004acb3 Mon Sep 17 00:00:00 2001 From: sonsumin Date: Wed, 20 Nov 2024 13:07:25 +0900 Subject: [PATCH 25/73] =?UTF-8?q?[#5]=E2=9C=A8Feat:=20ErrorCode=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yesummit/global/exception/ErrorCode.java | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 src/main/java/univ/yesummit/global/exception/ErrorCode.java diff --git a/src/main/java/univ/yesummit/global/exception/ErrorCode.java b/src/main/java/univ/yesummit/global/exception/ErrorCode.java new file mode 100644 index 0000000..ce66e18 --- /dev/null +++ b/src/main/java/univ/yesummit/global/exception/ErrorCode.java @@ -0,0 +1,26 @@ +package univ.yesummit.global.exception; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public enum ErrorCode { + + /* 사용자 */ + ALREADY_EXIST_MEMBER(5000, "이미 존재하는 사용자입니다."), + NOT_FOUND_MEMBER(5001, "사용자를 찾을 수 없습니다."), + MISMATCH_PASSWORD(5002, "비밀번호가 일치하지 않습니다."), + MISMATCH_EMAIL(5003, "이메일이 일치하지 않습니다."), + OAUTH2_LOGIN_FAILED(5004, "로그인에 실패했습니다."), + OAUTH2_REGISTRATION_FAILED(5005, "회원가입에 실패했습니다."), + + + /* 공용 */ + NOT_FOUND_ACCESS_TOKEN(00, "토큰을 찾을 수 없습니다."), + EXPIRED_ACCESS_TOKEN(403, "토큰의 유효시간이 만료되었습니다."), + INVALID_ACCESS_TOKEN(00, "유효하지 않은 토큰입니다."); + + private final int errorCode; + private final String message; +} From bce940fa1f79f07c90695d29d4fdb0c33af2876b Mon Sep 17 00:00:00 2001 From: sonsumin Date: Wed, 20 Nov 2024 13:07:32 +0900 Subject: [PATCH 26/73] =?UTF-8?q?[#5]=E2=9C=A8Feat:=20ErrorController=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/exception/ErrorController.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 src/main/java/univ/yesummit/global/exception/ErrorController.java diff --git a/src/main/java/univ/yesummit/global/exception/ErrorController.java b/src/main/java/univ/yesummit/global/exception/ErrorController.java new file mode 100644 index 0000000..caae5f5 --- /dev/null +++ b/src/main/java/univ/yesummit/global/exception/ErrorController.java @@ -0,0 +1,15 @@ +package univ.yesummit.global.exception; + +import io.swagger.v3.oas.annotations.Operation; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; + +@Controller +public class ErrorController { + + @Operation(summary = " 접근 거부 처리 ", description = " 접근 거부 처리 ") + @GetMapping("/access-denied") + public String accessDenied() { + return "access-denied"; // 뷰 이름 반환 + } +} From 1ed71d2289e43bee78ed7fdb71e42f5189a99263 Mon Sep 17 00:00:00 2001 From: sonsumin Date: Wed, 20 Nov 2024 13:07:42 +0900 Subject: [PATCH 27/73] =?UTF-8?q?[#5]=E2=9C=A8Feat:=20ErrorDTO=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yesummit/global/exception/dto/ErrorResponseVO.java | 10 ++++++++++ .../univ/yesummit/global/exception/dto/ResponseVO.java | 5 +++++ 2 files changed, 15 insertions(+) create mode 100644 src/main/java/univ/yesummit/global/exception/dto/ErrorResponseVO.java create mode 100644 src/main/java/univ/yesummit/global/exception/dto/ResponseVO.java diff --git a/src/main/java/univ/yesummit/global/exception/dto/ErrorResponseVO.java b/src/main/java/univ/yesummit/global/exception/dto/ErrorResponseVO.java new file mode 100644 index 0000000..7ecb43c --- /dev/null +++ b/src/main/java/univ/yesummit/global/exception/dto/ErrorResponseVO.java @@ -0,0 +1,10 @@ +package univ.yesummit.global.exception.dto; + +import lombok.Builder; + +@Builder +public record ErrorResponseVO ( + String name, + int errorCode, + String message +) {} diff --git a/src/main/java/univ/yesummit/global/exception/dto/ResponseVO.java b/src/main/java/univ/yesummit/global/exception/dto/ResponseVO.java new file mode 100644 index 0000000..a2240c9 --- /dev/null +++ b/src/main/java/univ/yesummit/global/exception/dto/ResponseVO.java @@ -0,0 +1,5 @@ +package univ.yesummit.global.exception.dto; + +public record ResponseVO ( + T data +) {} From e3244629a7c3e7c6216cee44a6b9cae13053caa9 Mon Sep 17 00:00:00 2001 From: sonsumin Date: Wed, 20 Nov 2024 13:07:51 +0900 Subject: [PATCH 28/73] =?UTF-8?q?[#5]=E2=9C=A8Feat:=20MemberException?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/member/exception/MemberException.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/main/java/univ/yesummit/domain/member/exception/MemberException.java diff --git a/src/main/java/univ/yesummit/domain/member/exception/MemberException.java b/src/main/java/univ/yesummit/domain/member/exception/MemberException.java new file mode 100644 index 0000000..5d9c8cf --- /dev/null +++ b/src/main/java/univ/yesummit/domain/member/exception/MemberException.java @@ -0,0 +1,12 @@ +package univ.yesummit.domain.member.exception; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import univ.yesummit.global.exception.ErrorCode; + +@Getter +@RequiredArgsConstructor +public class MemberException extends RuntimeException { + + private final ErrorCode errorCode; +} From 68b6ac0bf1491120262f9990dcad85778d080522 Mon Sep 17 00:00:00 2001 From: sonsumin Date: Wed, 20 Nov 2024 13:08:04 +0900 Subject: [PATCH 29/73] =?UTF-8?q?[#3]=E2=9C=A8Feat:=20MemberInfoDTO=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/member/dto/MemberInfoDTO.java | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 src/main/java/univ/yesummit/domain/member/dto/MemberInfoDTO.java diff --git a/src/main/java/univ/yesummit/domain/member/dto/MemberInfoDTO.java b/src/main/java/univ/yesummit/domain/member/dto/MemberInfoDTO.java new file mode 100644 index 0000000..f1e16b5 --- /dev/null +++ b/src/main/java/univ/yesummit/domain/member/dto/MemberInfoDTO.java @@ -0,0 +1,29 @@ +package univ.yesummit.domain.member.dto; + +import univ.yesummit.domain.member.entity.Member; + +public record MemberInfoDTO( + String name, + String phoneNumber, + String company, + String position, + String userType, + String businessRegistrationNumber, + String businessIdeaField, + boolean consentSummitAlerts, + boolean consentPrivacyPolicy +) { + public MemberInfoDTO(Member member) { + this( + member.getUsername(), + member.getPhoneNumber(), + member.getCompany(), + member.getPosition(), + member.getUserType().toString(), + member.getBusinessRegistrationNumber(), + member.getBusinessIdeaField(), + member.isConsentSummitAlerts(), + member.isConsentPrivacyPolicy() + ); + } +} From af3f38b7fc2c0601127a10908af75ca6d4a74a6c Mon Sep 17 00:00:00 2001 From: sonsumin Date: Wed, 20 Nov 2024 13:08:28 +0900 Subject: [PATCH 30/73] =?UTF-8?q?[#3]=E2=9C=A8Feat:=20AuthArgumentResolver?= =?UTF-8?q?=EB=A5=BC=20=EC=9C=84=ED=95=9C=20@User?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/univ/yesummit/global/resolver/User.java | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 src/main/java/univ/yesummit/global/resolver/User.java diff --git a/src/main/java/univ/yesummit/global/resolver/User.java b/src/main/java/univ/yesummit/global/resolver/User.java new file mode 100644 index 0000000..52fb874 --- /dev/null +++ b/src/main/java/univ/yesummit/global/resolver/User.java @@ -0,0 +1,11 @@ +package univ.yesummit.global.resolver; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.PARAMETER) +@Retention(RetentionPolicy.RUNTIME) +public @interface User { +} From 5f69f61b9fde79d018b726ea1c3cc88bbd1d85eb Mon Sep 17 00:00:00 2001 From: sonsumin Date: Wed, 20 Nov 2024 13:08:39 +0900 Subject: [PATCH 31/73] =?UTF-8?q?[#3]=E2=9C=A8Feat:=20UserType=EC=9E=91?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/univ/yesummit/domain/member/entity/UserType.java | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 src/main/java/univ/yesummit/domain/member/entity/UserType.java diff --git a/src/main/java/univ/yesummit/domain/member/entity/UserType.java b/src/main/java/univ/yesummit/domain/member/entity/UserType.java new file mode 100644 index 0000000..3d73620 --- /dev/null +++ b/src/main/java/univ/yesummit/domain/member/entity/UserType.java @@ -0,0 +1,6 @@ +package univ.yesummit.domain.member.entity; + +public enum UserType { + ENTREPRENEUR, //청년 창업가 + INVESTOR //예비 투자자 +} From bde4e727d6c8bb796cf7b9a3e0cb28c03a69931a Mon Sep 17 00:00:00 2001 From: sonsumin Date: Wed, 20 Nov 2024 13:08:49 +0900 Subject: [PATCH 32/73] =?UTF-8?q?[#3]=E2=9C=A8Feat:=20Member=20=EC=97=94?= =?UTF-8?q?=EB=93=9C=ED=8F=AC=EC=9D=B8=ED=8A=B8=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/controller/MemberController.java | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 src/main/java/univ/yesummit/domain/member/controller/MemberController.java diff --git a/src/main/java/univ/yesummit/domain/member/controller/MemberController.java b/src/main/java/univ/yesummit/domain/member/controller/MemberController.java new file mode 100644 index 0000000..eedb86c --- /dev/null +++ b/src/main/java/univ/yesummit/domain/member/controller/MemberController.java @@ -0,0 +1,71 @@ +package univ.yesummit.domain.member.controller; + +import io.swagger.v3.oas.annotations.Operation; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.*; +import univ.yesummit.domain.member.dto.MemberInfoDTO; +import univ.yesummit.domain.member.dto.MemberSignUpDTO; +import univ.yesummit.domain.member.dto.MemberUpdateDTO; +import univ.yesummit.domain.member.dto.MemberWithdrawDTO; +import univ.yesummit.domain.member.service.MemberService; +import univ.yesummit.global.oauth.OAuth2Member; +import univ.yesummit.global.resolver.LoginUser; +import univ.yesummit.global.resolver.User; + +@Slf4j +@RestController +@RequiredArgsConstructor +@RequestMapping("/v1/api/member") +public class MemberController { + + private final MemberService memberService; + + /** + * 첫 소셜 로그인 시에 추가적인 정보 수집(회원가입)진행 + */ + @PostMapping("/saveAdditionalInfo") + public void saveAdditionalInfo(@User LoginUser loginUser, @Valid @RequestBody MemberSignUpDTO memberSignUpDTO) throws Exception { + Long memberId = loginUser.getMemberId(); + memberService.saveAdditionalInfo(memberSignUpDTO, memberId); + } + + /** + * 회원정보수정 + */ + @PutMapping + @Operation(summary = "회원 정보 수정", description = "회원 정보를 수정합니다.") + @ResponseStatus(HttpStatus.OK) + public void updateInfo(@Valid @RequestBody MemberUpdateDTO memberUpdateDTO) throws Exception { + memberService.updateInfo(memberUpdateDTO); + } + + /** + * 회원탈퇴 + */ + @DeleteMapping + @Operation(summary = "회원 탈퇴", description = "회원탈퇴를 합니다.") + @ResponseStatus(HttpStatus.OK) + public void withdraw(@User LoginUser loginUser, @Valid @RequestBody MemberWithdrawDTO memberWithdrawDTO) throws Exception { + Long memberId = loginUser.getMemberId(); + memberService.withdraw(memberId, memberWithdrawDTO); + } + + /** + * 내정보조회 + */ + @GetMapping + @Operation(summary = "내 정보 조회", description = "내 정보를 조회합니다.") + @ResponseStatus(HttpStatus.OK) + public ResponseEntity getMyInfo(@User LoginUser loginUser) throws Exception { + Long memberId = loginUser.getMemberId(); + log.info(memberId.toString()); + MemberInfoDTO myInfo = memberService.getMyInfo(memberId); + return ResponseEntity.ok(myInfo); + } +} + From 5a6eafb2eaa82a879437dc5b7f0f52d666a99489 Mon Sep 17 00:00:00 2001 From: sonsumin Date: Wed, 20 Nov 2024 13:08:56 +0900 Subject: [PATCH 33/73] =?UTF-8?q?[#3]=E2=9C=A8Feat:=20Member=20=EC=97=94?= =?UTF-8?q?=ED=8B=B0=ED=8B=B0=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yesummit/domain/member/entity/Member.java | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 src/main/java/univ/yesummit/domain/member/entity/Member.java diff --git a/src/main/java/univ/yesummit/domain/member/entity/Member.java b/src/main/java/univ/yesummit/domain/member/entity/Member.java new file mode 100644 index 0000000..00b9a41 --- /dev/null +++ b/src/main/java/univ/yesummit/domain/member/entity/Member.java @@ -0,0 +1,96 @@ +package univ.yesummit.domain.member.entity; + +import jakarta.persistence.*; +import lombok.*; + +@Entity +@Getter +@Builder +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor +public class Member { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "member_id", unique = true, nullable = false) + private Long id; + + @Column(nullable = false) + private String username; + + @Column(nullable = false, unique = true) + private String email; + + private Authority authority; //ADMIN, USER, SIGN_OUT + + private String provider; + + private String providerId; + + /** + * 추가 정보 필드 + */ + private String company; //회사 정보 + + private String position; //회사 내 직책 + + private String phoneNumber; //전화번호 + + @Enumerated(EnumType.STRING) + private UserType userType; // 청년 창업가 or 예비 투자자 + + private String businessRegistrationNumber; + + private String businessIdeaField; //사업 아이디어 분야 + + private boolean consentSummitAlerts; //알림톡 수신 여부 + + private boolean consentPrivacyPolicy; //개인정보 수집 및 이용 동의 + + /** + * 회원 정보 수정 + * 서비스 특성상 최초 로그인 시 입력받은 정보 중 일부만 수정 가능하게 하였음 + */ + + public void updateEmail(String newEmail) { + this.email = newEmail; + } + + public void updateConsentSummitAlerts(boolean newConsentSummitAlerts) { + this.consentSummitAlerts = newConsentSummitAlerts; + } + + public void updatePhoneNumber(String newPhoneNumber) { + this.phoneNumber = newPhoneNumber; + } + + public void updateBusinessIdeaField(String newBusinessIdeaField) { + this.businessIdeaField = newBusinessIdeaField; + } + + //== 회원 가입 시에 USER권한을 부여 ==// + public void addUserAuthority() { + this.authority = Authority.USER; + } + + //== 회원 추가 정보 ==// + public void updateAdditionalInfo( + String phoneNumber, + UserType userType, + String company, + String position, + String businessRegistrationNumber, + String businessIdeaField, + Boolean consentSummitAlerts, + Boolean consentPrivacyPolicy + ) { + this.phoneNumber = phoneNumber; + this.userType = userType; + this.company = company; + this.position = position; + this.businessRegistrationNumber = businessRegistrationNumber; + this.businessIdeaField = businessIdeaField; + this.consentSummitAlerts = consentSummitAlerts; + this.consentPrivacyPolicy = consentPrivacyPolicy; + } +} From c3e68dfc35d74525bf59602dd261aaba407290d4 Mon Sep 17 00:00:00 2001 From: sonsumin Date: Wed, 20 Nov 2024 13:09:08 +0900 Subject: [PATCH 34/73] =?UTF-8?q?[#5]=E2=9C=A8Feat:=20ExceptionHandler=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../exception/GlobalExceptionHandler.java | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 src/main/java/univ/yesummit/global/exception/GlobalExceptionHandler.java diff --git a/src/main/java/univ/yesummit/global/exception/GlobalExceptionHandler.java b/src/main/java/univ/yesummit/global/exception/GlobalExceptionHandler.java new file mode 100644 index 0000000..5f1e56f --- /dev/null +++ b/src/main/java/univ/yesummit/global/exception/GlobalExceptionHandler.java @@ -0,0 +1,63 @@ +package univ.yesummit.global.exception; + +import com.auth0.jwt.exceptions.TokenExpiredException; +import io.jsonwebtoken.JwtException; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import univ.yesummit.domain.member.exception.MemberException; +import univ.yesummit.global.exception.dto.ErrorResponseVO; + + +import java.util.Objects; + +@RestControllerAdvice +public class GlobalExceptionHandler { + + /** + * 전역 예외 처리를 담당하는 GlobalExceptionHandler 클래스 + * @RestControllerAdvice를 통해 전역적으로 예외를 처리할 수 있는 어노테이션을 사용 + * 각 예외 처리 메서드는 특정한 예외가 발생했을 때 처리하고, + * ErrorResponseVO라는 객체를 반환하여 일관된 에러 응답을 제공 + */ + + @ExceptionHandler(MemberException.class) + public ErrorResponseVO handleMemberException(MemberException e) { + ErrorCode errorCode = e.getErrorCode(); + + return getErrorResponse(errorCode); + } + + @ExceptionHandler(value = JwtException.class) + public ResponseEntity handleJwtException(JwtException ex) { + return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid or expired token."); + } + + + @ResponseStatus(org.springframework.http.HttpStatus.FORBIDDEN) + @ExceptionHandler(TokenExpiredException.class) + public ErrorResponseVO handleTokenExpiredException(TokenExpiredException ex) { + return ErrorResponseVO.builder() + .name(ErrorCode.EXPIRED_ACCESS_TOKEN.name()) + .errorCode(ErrorCode.EXPIRED_ACCESS_TOKEN.getErrorCode()) + .message(ErrorCode.EXPIRED_ACCESS_TOKEN.getMessage()).build(); + } + + @ExceptionHandler(MethodArgumentNotValidException.class) + public ErrorResponseVO handleValidationException(MethodArgumentNotValidException ex) { + return ErrorResponseVO.builder() + .name("VALIDATION_ERROR") + .errorCode(ex.getStatusCode().value()) + .message(Objects.requireNonNull(ex.getFieldError()).getDefaultMessage()).build(); + } + + private ErrorResponseVO getErrorResponse(ErrorCode errorCode) { + return ErrorResponseVO.builder() + .name(errorCode.name()) + .errorCode(errorCode.getErrorCode()) + .message(errorCode.getMessage()).build(); + } +} From d76d78abe52029b1f460a20e4bb3a2f90e774334 Mon Sep 17 00:00:00 2001 From: sonsumin Date: Wed, 20 Nov 2024 13:09:17 +0900 Subject: [PATCH 35/73] =?UTF-8?q?[#5]=E2=9C=A8Feat:=20LoginUser=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/univ/yesummit/global/resolver/LoginUser.java | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 src/main/java/univ/yesummit/global/resolver/LoginUser.java diff --git a/src/main/java/univ/yesummit/global/resolver/LoginUser.java b/src/main/java/univ/yesummit/global/resolver/LoginUser.java new file mode 100644 index 0000000..5e98417 --- /dev/null +++ b/src/main/java/univ/yesummit/global/resolver/LoginUser.java @@ -0,0 +1,11 @@ +package univ.yesummit.global.resolver; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class LoginUser { + + private final Long memberId; +} From 30921d621d8b83c9b303bffac61c3e89f944eb54 Mon Sep 17 00:00:00 2001 From: sonsumin Date: Wed, 20 Nov 2024 13:10:38 +0900 Subject: [PATCH 36/73] =?UTF-8?q?[#6]=E2=9C=A8Feat:=20S3=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=EC=9D=84=20=EC=9C=84=ED=95=9C=20config?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yesummit/global/s3/config/S3Config.java | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 src/main/java/univ/yesummit/global/s3/config/S3Config.java diff --git a/src/main/java/univ/yesummit/global/s3/config/S3Config.java b/src/main/java/univ/yesummit/global/s3/config/S3Config.java new file mode 100644 index 0000000..19ef407 --- /dev/null +++ b/src/main/java/univ/yesummit/global/s3/config/S3Config.java @@ -0,0 +1,33 @@ +package univ.yesummit.global.s3.config; + +import com.amazonaws.auth.AWSCredentials; +import com.amazonaws.auth.AWSStaticCredentialsProvider; +import com.amazonaws.auth.BasicAWSCredentials; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.AmazonS3ClientBuilder; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class S3Config { + + @Value("${cloud.aws.credentials.accessKey}") + private String accessKey; + + @Value("${cloud.aws.credentials.secretKey}") + private String secretKey; + + @Value("${cloud.aws.region.static}") + private String region; + + @Bean + public AmazonS3 amazonS3() { + AWSCredentials awsCredentials = new BasicAWSCredentials(accessKey, secretKey); + return AmazonS3ClientBuilder + .standard() + .withRegion(region) + .withCredentials(new AWSStaticCredentialsProvider(awsCredentials)) + .build(); + } +} From 1404487e08f4d96b4d86d41994ee55d16a8f38d0 Mon Sep 17 00:00:00 2001 From: sonsumin Date: Wed, 20 Nov 2024 13:10:43 +0900 Subject: [PATCH 37/73] =?UTF-8?q?[#6]=E2=9C=A8Feat:=20S3=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=EC=9D=84=20=EC=9C=84=ED=95=9C=20service?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yesummit/global/s3/service/S3Service.java | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 src/main/java/univ/yesummit/global/s3/service/S3Service.java diff --git a/src/main/java/univ/yesummit/global/s3/service/S3Service.java b/src/main/java/univ/yesummit/global/s3/service/S3Service.java new file mode 100644 index 0000000..0f09f82 --- /dev/null +++ b/src/main/java/univ/yesummit/global/s3/service/S3Service.java @@ -0,0 +1,91 @@ +package univ.yesummit.global.s3.service; + +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.model.CannedAccessControlList; +import com.amazonaws.services.s3.model.ObjectMetadata; +import com.amazonaws.services.s3.model.PutObjectRequest; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; +import java.util.List; +import java.util.UUID; + +@Service +@RequiredArgsConstructor +public class S3Service { + + private final AmazonS3 amazonS3; + @Value("${cloud.aws.s3.bucket}") + private String bucket; + + /** + * 이미지를 S3에 업로드하고 URL을 반환 + * @param multipartFile + * @param folder + * @return + */ + public String uploadImage(MultipartFile multipartFile, String folder) { + String fileName = createFileName(multipartFile.getOriginalFilename()); + ObjectMetadata objectMetadata = new ObjectMetadata(); + objectMetadata.setContentLength(multipartFile.getSize()); + objectMetadata.setContentType(multipartFile.getContentType()); + + try (InputStream inputStream = multipartFile.getInputStream()) { + amazonS3.putObject(new PutObjectRequest(bucket + "/" + folder + "/image", fileName, inputStream, objectMetadata) + .withCannedAcl(CannedAccessControlList.PublicRead)); + return amazonS3.getUrl(bucket + "/" + folder + "/image", fileName).toString(); + } catch (IOException e) { + throw new RuntimeException("이미지 업로드 중 오류가 발생했습니다.", e); + } + } + + /** + * 고유한 파일 이름을 생성 + * @param originalFileName + * @return + */ + private String createFileName(String originalFileName) { + return UUID.randomUUID().toString().concat(getFileExtension(originalFileName)); + } + + /** + * 파일 확장자를 검증 + * @param fileName + * @return + */ + private String getFileExtension(String fileName) { + if (fileName.length() == 0) { + throw new RuntimeException("파일 이름이 유효하지 않습니다."); + } + String extension = fileName.substring(fileName.lastIndexOf(".")); + List validExtensions = Arrays.asList(".jpg", ".jpeg", ".png", ".gif", ".JPG", ".JPEG", ".PNG", ".GIF"); + if (!validExtensions.contains(extension)) { + throw new RuntimeException("지원하지 않는 파일 형식입니다."); + } + return extension; + } + + /** + * S3에서 이미지를 삭제합니다. + * @param imageUrl + */ + public void deleteImage(String imageUrl) { + // 버킷의 url을 가져옴 + String bucketUrl = amazonS3.getUrl(bucket, "").toString(); + // 이미지 url에서 버킷 url을 제거하여 이미지 키를 얻음 + String imageKey = imageUrl.replace(bucketUrl, ""); + + // 이미지 키가 '/'로 시작하면 제거 + if (imageKey.startsWith("/")) { + imageKey = imageKey.substring(1); + } + + // S3에서 객체를 삭제 + amazonS3.deleteObject(bucket, imageKey); + } +} From c8d5561c88fb454b83f44b94e49eb0cc15259f14 Mon Sep 17 00:00:00 2001 From: Soomin Date: Wed, 20 Nov 2024 13:20:02 +0900 Subject: [PATCH 38/73] =?UTF-8?q?[#2]=E2=9C=A8Feat:=20Workflow=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/gradle.yml | 144 +++++++++++++++++++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 .github/workflows/gradle.yml diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml new file mode 100644 index 0000000..6d68497 --- /dev/null +++ b/.github/workflows/gradle.yml @@ -0,0 +1,144 @@ +# 필요한 Repo Secret 설정 +#### CI +# ${{ secrets.SUBMODULE_ACCESS_TOKEN }} : 깃허브 액세스 토큰 + +#### CD +# ${{ secrets.DOCKER_ID }} : 도커허브 id +# ${{ secrets.DOCKER_PASSWORD }} : 도커허브 pw +# ${{ secrets.REMOTE_HOST_DEV }} : 배포 서버 HOSTNAME +# ${{ secrets.REMOTE_PORT_DEV }} : 배포 서버 PORT +# ${{ secrets.REMOTE_USERNAME_DEV }} : 배포 서버 USERNAME +# ${{ secrets.REMOTE_SSH_KEY_DEV }} : 배포 서버 연결을 위한 SSH KEY + +name: Backend CI & CD (dev) + +on: + pull_request: + branches: [dev] + push: + branches: [dev] + +env: + CONTAINER_NAME: yesummit + +jobs: + Continuous-Integration: + env: + PR_NUMBER: ${{ github.event.pull_request.number }} + # CI 실행 (환경은 github 제공) + runs-on: ubuntu-20.04 + steps: + + # 소스코드 체크아웃 + - name: Checkout source code + uses: actions/checkout@v4 + with: + submodules: true + token: ${{ secrets.ACTION_TOKEN }} + ref: ${{ github.head_ref }} + + - name: Install JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'zulu' + cache: 'gradle' + + # Gradle Package Caching + - name: Caching Gradle packages + uses: actions/cache@v3 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + + - name: Grant execute permission for gradle + run: chmod +x ./gradlew + + # develop 브랜치일 경우 dev 환경 빌드 + - name: create build file + run: ./gradlew clean build -i --no-daemon -Dspring.profiles.active=prod + + # push event일 경우 CD job에 jar file 업로드 + - name: (Push) Archive production artifacts + if: github.event_name == 'push' + uses: actions/upload-artifact@v4 + with: + name: build + path: build/libs/*.jar + + Continuous-Deploy: + # push 하는 경우에만 배포 JOB 실행 + if: github.event_name == 'push' + needs: Continuous-Integration + runs-on: ubuntu-latest + steps: + + # 소스코드 가져오기 + - name: Checkout source code + uses: actions/checkout@v4 + + # 이전 Job에서 업로드한 Jar file 다운로드 + - name : Download a built Jar File + uses: actions/download-artifact@v4 + with: + name: build + path: build/libs + + # Docker Buildx Setting + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + # Docker Login + - name: Docker Login + uses: docker/login-action@v2.1.0 + with: + # Username used to log against the Docker registry + username: ${{ secrets.DOCKER_ID }} + # Password or personal access token used to log against the Docker registry + password: ${{ secrets.DOCKER_PASSWORD }} + + # Docker Build & Push + - name: Docker Build and push + uses: docker/build-push-action@v5 + with: + context: . + file: ./Dockerfile-dev + platforms: linux/amd64 + push: true + tags: | + ${{ secrets.DOCKER_ID }}/${{ env.CONTAINER_NAME }}:${{github.run_number}} + ${{ secrets.DOCKER_ID }}/${{ env.CONTAINER_NAME }}:latest + cache-from: type=gha # gha=Github Action Cache + cache-to: type=gha,mode=max + + - name: Create and execute deploy script + run: | + echo '#!/bin/bash' > deploy.sh + echo 'sudo docker rm -f ${{ env.CONTAINER_NAME }}' >> deploy.sh + echo 'sudo docker rmi ${{ secrets.DOCKER_ID }}/${{ env.CONTAINER_NAME }}' >> deploy.sh + echo 'sudo docker pull ${{ secrets.DOCKER_ID }}/${{ env.CONTAINER_NAME }}' >> deploy.sh + echo 'sudo docker run -d -p 8080:8080 --add-host host.docker.internal:host-gateway --restart=unless-stopped --log-opt max-size=10m --log-opt max-file=3 --name ${{ env.CONTAINER_NAME }} ${{ secrets.DOCKER_ID }}/${{ env.CONTAINER_NAME }}' >> deploy.sh + + - name: Transfer Deploy Script use SCP + uses: appleboy/scp-action@master + with: + host: ${{ secrets.REMOTE_HOST_DEV }} + port: ${{ secrets.REMOTE_PORT_DEV }} + username: ${{ secrets.REMOTE_USERNAME_DEV }} + key: ${{ secrets.REMOTE_SSH_KEY_DEV }} + source: deploy.sh + target: /home/${{ secrets.REMOTE_USERNAME_DEV }}/deploy + + # SSH Connect + - name: Execute Server Init Script + uses: appleboy/ssh-action@master + with: + host: ${{ secrets.REMOTE_HOST_DEV }} + port: ${{ secrets.REMOTE_PORT_DEV }} + username: ${{ secrets.REMOTE_USERNAME_DEV }} + key: ${{ secrets.REMOTE_SSH_KEY_DEV }} + script_stop: true + script: | + chmod +x /home/${{ secrets.REMOTE_USERNAME_DEV }}/deploy/deploy.sh && sh /home/${{ secrets.REMOTE_USERNAME_DEV }}/deploy/deploy.sh From e3440cc7fad76f8df3e1fddd7ab6dff56b6701c1 Mon Sep 17 00:00:00 2001 From: sonsumin Date: Wed, 20 Nov 2024 13:36:25 +0900 Subject: [PATCH 39/73] =?UTF-8?q?[#1]=E2=9C=85Test:=20CI/CD=20Test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/{gradle.yml => deploy-prod.yml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{gradle.yml => deploy-prod.yml} (100%) diff --git a/.github/workflows/gradle.yml b/.github/workflows/deploy-prod.yml similarity index 100% rename from .github/workflows/gradle.yml rename to .github/workflows/deploy-prod.yml From 1e132fa395563a1b1988e633609243558f25454c Mon Sep 17 00:00:00 2001 From: sonsumin Date: Wed, 20 Nov 2024 14:34:19 +0900 Subject: [PATCH 40/73] =?UTF-8?q?[#1]=E2=9C=85Test:=20CI/CD=20Test2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../univ/yesummit/global/auth/controller/AuthController.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/univ/yesummit/global/auth/controller/AuthController.java b/src/main/java/univ/yesummit/global/auth/controller/AuthController.java index 5fafdd2..896c999 100644 --- a/src/main/java/univ/yesummit/global/auth/controller/AuthController.java +++ b/src/main/java/univ/yesummit/global/auth/controller/AuthController.java @@ -13,4 +13,5 @@ public class AuthController { public RedirectView login() { return new RedirectView("/oauth2/authorization/kakao"); } + //check1 } \ No newline at end of file From bb8fba6a7cdc925f038aa0a22f7b0658ae0f4b35 Mon Sep 17 00:00:00 2001 From: sonsumin Date: Wed, 20 Nov 2024 14:42:34 +0900 Subject: [PATCH 41/73] =?UTF-8?q?[#1]=E2=9C=85Test:=20CI/CD=20Test3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/auth/config/WebMvcConfig.java | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/main/java/univ/yesummit/global/auth/config/WebMvcConfig.java b/src/main/java/univ/yesummit/global/auth/config/WebMvcConfig.java index e132a2b..2420cbb 100644 --- a/src/main/java/univ/yesummit/global/auth/config/WebMvcConfig.java +++ b/src/main/java/univ/yesummit/global/auth/config/WebMvcConfig.java @@ -17,14 +17,15 @@ public class WebMvcConfig implements WebMvcConfigurer { private final AuthArgumentResolver authArgumentResolver; - @Override - public void addCorsMappings(final CorsRegistry registry ){ - registry.addMapping("/**") - .allowedOriginPatterns("*") - .allowedMethods("PATCH","GET","POST","PUT","DELETE","HEAD","OPTIONS") - .allowedHeaders("*") - .allowCredentials(true); - } +// +// @Override +// public void addCorsMappings(final CorsRegistry registry ){ +// registry.addMapping("/**") +// .allowedOriginPatterns("*") +// .allowedMethods("PATCH","GET","POST","PUT","DELETE","HEAD","OPTIONS") +// .allowedHeaders("*") +// .allowCredentials(true); +// } @Override public void addArgumentResolvers(List resolvers) { From dd41d8af454a901a17336b04aee8f4ede14bd59b Mon Sep 17 00:00:00 2001 From: sonsumin Date: Wed, 20 Nov 2024 14:58:26 +0900 Subject: [PATCH 42/73] =?UTF-8?q?[#1]=E2=9C=85Test:=20CI/CD=20Test4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/univ/yesummit/YesummitApplication.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/univ/yesummit/YesummitApplication.java b/src/main/java/univ/yesummit/YesummitApplication.java index 39a4fc6..8445041 100644 --- a/src/main/java/univ/yesummit/YesummitApplication.java +++ b/src/main/java/univ/yesummit/YesummitApplication.java @@ -2,7 +2,9 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing; +@EnableJpaAuditing @SpringBootApplication public class YesummitApplication { From fd5b1dcdf022938de581b196ef7f06d61eae9686 Mon Sep 17 00:00:00 2001 From: sonsumin Date: Wed, 20 Nov 2024 15:58:48 +0900 Subject: [PATCH 43/73] =?UTF-8?q?[#1]=E2=9C=85Test:=20CI/CD=20Test5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/univ/yesummit/global/auth/util/JwtUtils.java | 6 ++++++ src/main/resources/application.properties | 1 - 2 files changed, 6 insertions(+), 1 deletion(-) delete mode 100644 src/main/resources/application.properties diff --git a/src/main/java/univ/yesummit/global/auth/util/JwtUtils.java b/src/main/java/univ/yesummit/global/auth/util/JwtUtils.java index 27238ee..38ecb55 100644 --- a/src/main/java/univ/yesummit/global/auth/util/JwtUtils.java +++ b/src/main/java/univ/yesummit/global/auth/util/JwtUtils.java @@ -5,6 +5,7 @@ import com.auth0.jwt.interfaces.DecodedJWT; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; +import jakarta.annotation.PostConstruct; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import jakarta.transaction.Transactional; @@ -48,6 +49,11 @@ public class JwtUtils { private final MemberRepository memberRepository; + @PostConstruct + public void init() { + log.info("JwtUtils initialized with secret: {}", secret); + } + public String createAccessToken(Long memberId) { log.info("memberId info: {}", memberId.toString()); diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties deleted file mode 100644 index b9983d7..0000000 --- a/src/main/resources/application.properties +++ /dev/null @@ -1 +0,0 @@ -spring.application.name=yesummit From 494b8f2a4c052644d66c21ae34b2a146bc90b022 Mon Sep 17 00:00:00 2001 From: sonsumin Date: Wed, 20 Nov 2024 16:03:55 +0900 Subject: [PATCH 44/73] =?UTF-8?q?[#1]=E2=9C=85Test:=20CI/CD=20Test6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy-prod.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/deploy-prod.yml b/.github/workflows/deploy-prod.yml index 6d68497..4660a05 100644 --- a/.github/workflows/deploy-prod.yml +++ b/.github/workflows/deploy-prod.yml @@ -20,6 +20,7 @@ on: env: CONTAINER_NAME: yesummit + JWT_SECRET: ${{ secrets.JWT_SECRET }} # JWT 환경변수 jobs: Continuous-Integration: From 63238101f9e1026c4f71d0803ccd01af1a9c86fa Mon Sep 17 00:00:00 2001 From: sonsumin Date: Wed, 20 Nov 2024 16:09:24 +0900 Subject: [PATCH 45/73] =?UTF-8?q?[#1]=E2=9C=85Test:=20CI/CD=20Test7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/auth/config/WebMvcConfig.java | 19 ++++++++--------- .../auth/controller/AuthController.java | 3 +-- .../yesummit/global/auth/util/JwtUtils.java | 8 +------ .../global/auth/util/SecurityUtil.java | 2 +- .../global/swagger/SwaggerConfig.java | 21 ------------------- 5 files changed, 12 insertions(+), 41 deletions(-) delete mode 100644 src/main/java/univ/yesummit/global/swagger/SwaggerConfig.java diff --git a/src/main/java/univ/yesummit/global/auth/config/WebMvcConfig.java b/src/main/java/univ/yesummit/global/auth/config/WebMvcConfig.java index 2420cbb..23777df 100644 --- a/src/main/java/univ/yesummit/global/auth/config/WebMvcConfig.java +++ b/src/main/java/univ/yesummit/global/auth/config/WebMvcConfig.java @@ -17,19 +17,18 @@ public class WebMvcConfig implements WebMvcConfigurer { private final AuthArgumentResolver authArgumentResolver; -// -// @Override -// public void addCorsMappings(final CorsRegistry registry ){ -// registry.addMapping("/**") -// .allowedOriginPatterns("*") -// .allowedMethods("PATCH","GET","POST","PUT","DELETE","HEAD","OPTIONS") -// .allowedHeaders("*") -// .allowCredentials(true); -// } + @Override + public void addCorsMappings(final CorsRegistry registry ){ + registry.addMapping("/**") + .allowedOriginPatterns("*") + .allowedMethods("PATCH","GET","POST","PUT","DELETE","HEAD","OPTIONS") + .allowedHeaders("*") + .allowCredentials(true); + } @Override public void addArgumentResolvers(List resolvers) { WebMvcConfigurer.super.addArgumentResolvers(resolvers); // 기존 Resolver resolvers.add(authArgumentResolver); // 커스텀 Resolver } -} \ No newline at end of file +} diff --git a/src/main/java/univ/yesummit/global/auth/controller/AuthController.java b/src/main/java/univ/yesummit/global/auth/controller/AuthController.java index 896c999..c37a438 100644 --- a/src/main/java/univ/yesummit/global/auth/controller/AuthController.java +++ b/src/main/java/univ/yesummit/global/auth/controller/AuthController.java @@ -13,5 +13,4 @@ public class AuthController { public RedirectView login() { return new RedirectView("/oauth2/authorization/kakao"); } - //check1 -} \ No newline at end of file +} diff --git a/src/main/java/univ/yesummit/global/auth/util/JwtUtils.java b/src/main/java/univ/yesummit/global/auth/util/JwtUtils.java index 38ecb55..36af6fa 100644 --- a/src/main/java/univ/yesummit/global/auth/util/JwtUtils.java +++ b/src/main/java/univ/yesummit/global/auth/util/JwtUtils.java @@ -5,7 +5,6 @@ import com.auth0.jwt.interfaces.DecodedJWT; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; -import jakarta.annotation.PostConstruct; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import jakarta.transaction.Transactional; @@ -49,11 +48,6 @@ public class JwtUtils { private final MemberRepository memberRepository; - @PostConstruct - public void init() { - log.info("JwtUtils initialized with secret: {}", secret); - } - public String createAccessToken(Long memberId) { log.info("memberId info: {}", memberId.toString()); @@ -166,4 +160,4 @@ public boolean isValid(String token) { } } //==========================================================================// -} \ No newline at end of file +} diff --git a/src/main/java/univ/yesummit/global/auth/util/SecurityUtil.java b/src/main/java/univ/yesummit/global/auth/util/SecurityUtil.java index 60d3bf5..6366429 100644 --- a/src/main/java/univ/yesummit/global/auth/util/SecurityUtil.java +++ b/src/main/java/univ/yesummit/global/auth/util/SecurityUtil.java @@ -24,4 +24,4 @@ public static String getLoginUsername() { throw new IllegalStateException("Unknown principal type"); } -} \ No newline at end of file +} diff --git a/src/main/java/univ/yesummit/global/swagger/SwaggerConfig.java b/src/main/java/univ/yesummit/global/swagger/SwaggerConfig.java deleted file mode 100644 index 537d890..0000000 --- a/src/main/java/univ/yesummit/global/swagger/SwaggerConfig.java +++ /dev/null @@ -1,21 +0,0 @@ -package univ.yesummit.global.swagger; - -import io.swagger.v3.oas.models.OpenAPI; -import io.swagger.v3.oas.models.info.Info; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - - -@Configuration -public class SwaggerConfig { - - @Bean - public OpenAPI openAPI() { - return new OpenAPI() - .info(new Info() - .version("v1.0.0") // 버전 기록 - .title("YE;Summit API 명세서") // API 명세서 제목 - .description("API 명세서")); // 상세명세서 - - } -} \ No newline at end of file From e03a38c853c9eb18e2c21ea9088861db7f246857 Mon Sep 17 00:00:00 2001 From: sonsumin Date: Wed, 20 Nov 2024 16:11:17 +0900 Subject: [PATCH 46/73] =?UTF-8?q?[#1]=E2=9C=85Chore:=20Dockerfile=EC=9E=91?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile-dev | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 Dockerfile-dev diff --git a/Dockerfile-dev b/Dockerfile-dev new file mode 100644 index 0000000..dbae776 --- /dev/null +++ b/Dockerfile-dev @@ -0,0 +1,10 @@ +FROM eclipse-temurin:17-jdk + +VOLUME /tmp + +ARG JAR_FILE=build/libs/*.jar + +COPY ${JAR_FILE} app.jar + +# 시간대 및 Spring 프로필 설정 +ENTRYPOINT ["java", "-jar", "-Dspring.profiles.active=prod", "-Duser.timezone=Asia/Seoul", "/app.jar"] \ No newline at end of file From dbb6e952a96c8a70884d45ae75398d8b4694729d Mon Sep 17 00:00:00 2001 From: sonsumin Date: Wed, 20 Nov 2024 16:18:20 +0900 Subject: [PATCH 47/73] =?UTF-8?q?[#1]=E2=9C=85Test:=20CI/CD=20Test=208?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index ee776e4..4e742f2 100644 --- a/build.gradle +++ b/build.gradle @@ -66,4 +66,8 @@ tasks.register('copyExternalProperties', Copy) { } processResources.dependsOn('copyExternalProperties') -processTestResources.dependsOn('copyExternalProperties') \ No newline at end of file +processTestResources.dependsOn('copyExternalProperties') + +test { + systemProperty "spring.profiles.active", "prod" +} \ No newline at end of file From f40cb5902ab1a65017c32810e2690fcb4495bc77 Mon Sep 17 00:00:00 2001 From: sonsumin Date: Wed, 20 Nov 2024 16:20:45 +0900 Subject: [PATCH 48/73] =?UTF-8?q?[#1]=E2=9C=85Test:=20CI/CD=20Test=209?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/univ/yesummit/YesummitApplicationTests.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/test/java/univ/yesummit/YesummitApplicationTests.java b/src/test/java/univ/yesummit/YesummitApplicationTests.java index 4fdf41b..25e6c00 100644 --- a/src/test/java/univ/yesummit/YesummitApplicationTests.java +++ b/src/test/java/univ/yesummit/YesummitApplicationTests.java @@ -2,7 +2,9 @@ import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; +@ActiveProfiles("prod") @SpringBootTest class YesummitApplicationTests { From a4365f6a8099d3ef01d6f85d7bc73b91c6f01e82 Mon Sep 17 00:00:00 2001 From: sonsumin Date: Wed, 20 Nov 2024 16:27:30 +0900 Subject: [PATCH 49/73] =?UTF-8?q?[#1]=E2=9C=85Test:=20CI/CD=20Test=2010?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy-prod.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy-prod.yml b/.github/workflows/deploy-prod.yml index 4660a05..4a98ea8 100644 --- a/.github/workflows/deploy-prod.yml +++ b/.github/workflows/deploy-prod.yml @@ -59,7 +59,7 @@ jobs: # develop 브랜치일 경우 dev 환경 빌드 - name: create build file - run: ./gradlew clean build -i --no-daemon -Dspring.profiles.active=prod + run: ./gradlew clean build -x test -i --no-daemon -Dspring.profiles.active=prod # push event일 경우 CD job에 jar file 업로드 - name: (Push) Archive production artifacts From 61f73f534780f0e421827193b2e0c4161a0f2043 Mon Sep 17 00:00:00 2001 From: sonsumin Date: Wed, 20 Nov 2024 16:32:26 +0900 Subject: [PATCH 50/73] =?UTF-8?q?[#1]=E2=9C=85Test:=20CI/CD=20Test=2011?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy-prod.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy-prod.yml b/.github/workflows/deploy-prod.yml index 4a98ea8..db8de38 100644 --- a/.github/workflows/deploy-prod.yml +++ b/.github/workflows/deploy-prod.yml @@ -20,7 +20,6 @@ on: env: CONTAINER_NAME: yesummit - JWT_SECRET: ${{ secrets.JWT_SECRET }} # JWT 환경변수 jobs: Continuous-Integration: @@ -58,6 +57,7 @@ jobs: run: chmod +x ./gradlew # develop 브랜치일 경우 dev 환경 빌드 + # 현재 테스트 코드를 따로 작성하지 않아. test 없이 빌드함 - name: create build file run: ./gradlew clean build -x test -i --no-daemon -Dspring.profiles.active=prod From 107a00249dd345a19e06a61e62ee837b7102d468 Mon Sep 17 00:00:00 2001 From: sonsumin Date: Wed, 20 Nov 2024 16:45:31 +0900 Subject: [PATCH 51/73] =?UTF-8?q?[#1]=E2=9C=85Test:=20CI/CD=20Test=2012?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/common/config/SwaggerConfig.java | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 src/main/java/univ/yesummit/global/common/config/SwaggerConfig.java diff --git a/src/main/java/univ/yesummit/global/common/config/SwaggerConfig.java b/src/main/java/univ/yesummit/global/common/config/SwaggerConfig.java new file mode 100644 index 0000000..fdad296 --- /dev/null +++ b/src/main/java/univ/yesummit/global/common/config/SwaggerConfig.java @@ -0,0 +1,21 @@ +package univ.yesummit.global.common.config; + +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.info.Info; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + + +@Configuration +public class SwaggerConfig { + + @Bean + public OpenAPI openAPI() { + return new OpenAPI() + .info(new Info() + .version("v1.0.0") // 버전 기록 + .title("YE;Summit API 명세서") // API 명세서 제목 + .description("API 명세서")); // 상세명세서 + + } +} From 72f892b2b9eb9de6fb76e7b1169f1033c17f9db9 Mon Sep 17 00:00:00 2001 From: sonsumin Date: Wed, 20 Nov 2024 16:54:21 +0900 Subject: [PATCH 52/73] =?UTF-8?q?[#1]=E2=9C=85Test:=20CI/CD=20Test=2014?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../univ/yesummit/global/auth/controller/AuthController.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/univ/yesummit/global/auth/controller/AuthController.java b/src/main/java/univ/yesummit/global/auth/controller/AuthController.java index c37a438..956f4a9 100644 --- a/src/main/java/univ/yesummit/global/auth/controller/AuthController.java +++ b/src/main/java/univ/yesummit/global/auth/controller/AuthController.java @@ -13,4 +13,5 @@ public class AuthController { public RedirectView login() { return new RedirectView("/oauth2/authorization/kakao"); } + // test13 } From 13dd4e589d8e9ca3f38d35bd576ed1ffb3e875a5 Mon Sep 17 00:00:00 2001 From: sonsumin Date: Wed, 20 Nov 2024 17:11:47 +0900 Subject: [PATCH 53/73] =?UTF-8?q?[#1]=E2=9C=85Test:=20CI/CD=20Test=2015?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../univ/yesummit/global/auth/controller/AuthController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/univ/yesummit/global/auth/controller/AuthController.java b/src/main/java/univ/yesummit/global/auth/controller/AuthController.java index 956f4a9..3e1456f 100644 --- a/src/main/java/univ/yesummit/global/auth/controller/AuthController.java +++ b/src/main/java/univ/yesummit/global/auth/controller/AuthController.java @@ -13,5 +13,5 @@ public class AuthController { public RedirectView login() { return new RedirectView("/oauth2/authorization/kakao"); } - // test13 + // test15 } From a319b33cb0c57f753b03884ac91adf71df960eff Mon Sep 17 00:00:00 2001 From: sonsumin Date: Wed, 20 Nov 2024 17:21:40 +0900 Subject: [PATCH 54/73] =?UTF-8?q?[#1]=E2=9C=85Test:=20CI/CD=20Test=2017?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../univ/yesummit/global/auth/controller/AuthController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/univ/yesummit/global/auth/controller/AuthController.java b/src/main/java/univ/yesummit/global/auth/controller/AuthController.java index 3e1456f..ff5aaa6 100644 --- a/src/main/java/univ/yesummit/global/auth/controller/AuthController.java +++ b/src/main/java/univ/yesummit/global/auth/controller/AuthController.java @@ -13,5 +13,5 @@ public class AuthController { public RedirectView login() { return new RedirectView("/oauth2/authorization/kakao"); } - // test15 + // test17 } From 897f507947d9b07a48587e16a320583a3299351e Mon Sep 17 00:00:00 2001 From: sonsumin Date: Wed, 20 Nov 2024 17:30:47 +0900 Subject: [PATCH 55/73] =?UTF-8?q?[#1]=E2=9C=85Test:=20CI/CD=20Test=2018?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../univ/yesummit/global/auth/controller/AuthController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/univ/yesummit/global/auth/controller/AuthController.java b/src/main/java/univ/yesummit/global/auth/controller/AuthController.java index ff5aaa6..cc12330 100644 --- a/src/main/java/univ/yesummit/global/auth/controller/AuthController.java +++ b/src/main/java/univ/yesummit/global/auth/controller/AuthController.java @@ -13,5 +13,5 @@ public class AuthController { public RedirectView login() { return new RedirectView("/oauth2/authorization/kakao"); } - // test17 + // test18 } From 0a0b807110036e2d801b60dd380f3101142adb46 Mon Sep 17 00:00:00 2001 From: sonsumin Date: Wed, 20 Nov 2024 17:44:38 +0900 Subject: [PATCH 56/73] =?UTF-8?q?[#1]=E2=9C=85Test:=20CI/CD=20Test=2019?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../univ/yesummit/global/auth/controller/AuthController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/univ/yesummit/global/auth/controller/AuthController.java b/src/main/java/univ/yesummit/global/auth/controller/AuthController.java index cc12330..62843aa 100644 --- a/src/main/java/univ/yesummit/global/auth/controller/AuthController.java +++ b/src/main/java/univ/yesummit/global/auth/controller/AuthController.java @@ -13,5 +13,5 @@ public class AuthController { public RedirectView login() { return new RedirectView("/oauth2/authorization/kakao"); } - // test18 + // test19 } From 27a5067845d792bd7ea7f98b63fa59db322e3f98 Mon Sep 17 00:00:00 2001 From: sonsumin Date: Wed, 20 Nov 2024 17:53:23 +0900 Subject: [PATCH 57/73] =?UTF-8?q?[#1]=E2=9C=85Test:=20CI/CD=20Test=2020?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../univ/yesummit/global/auth/controller/AuthController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/univ/yesummit/global/auth/controller/AuthController.java b/src/main/java/univ/yesummit/global/auth/controller/AuthController.java index 62843aa..e4579b9 100644 --- a/src/main/java/univ/yesummit/global/auth/controller/AuthController.java +++ b/src/main/java/univ/yesummit/global/auth/controller/AuthController.java @@ -13,5 +13,5 @@ public class AuthController { public RedirectView login() { return new RedirectView("/oauth2/authorization/kakao"); } - // test19 + // test20 } From 3660f926d6ba566a058e2b2c629198aee93d87be Mon Sep 17 00:00:00 2001 From: sonsumin Date: Wed, 20 Nov 2024 18:39:45 +0900 Subject: [PATCH 58/73] =?UTF-8?q?[#1]=E2=9C=85Test:=20CI/CD=20Test=2021?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../univ/yesummit/global/auth/controller/AuthController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/univ/yesummit/global/auth/controller/AuthController.java b/src/main/java/univ/yesummit/global/auth/controller/AuthController.java index e4579b9..6871305 100644 --- a/src/main/java/univ/yesummit/global/auth/controller/AuthController.java +++ b/src/main/java/univ/yesummit/global/auth/controller/AuthController.java @@ -13,5 +13,5 @@ public class AuthController { public RedirectView login() { return new RedirectView("/oauth2/authorization/kakao"); } - // test20 + // test21 } From 1bfcaca36f1bffcde8f6c50931cfaa5f90d23bcf Mon Sep 17 00:00:00 2001 From: sonsumin Date: Wed, 20 Nov 2024 19:13:58 +0900 Subject: [PATCH 59/73] =?UTF-8?q?[#1]=E2=99=BB=EF=B8=8FRefactor:=20?= =?UTF-8?q?=EB=B2=84=EC=A0=84=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 4e742f2..eb600aa 100644 --- a/build.gradle +++ b/build.gradle @@ -25,7 +25,7 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' // DB - runtimeOnly 'com.mysql:mysql-connector-j:8.3.0' + implementation 'mysql:mysql-connector-java:8.0.33' runtimeOnly 'com.h2database:h2' // p6spy 적용 From f1c10c933eacc3ed936d102e03320d9c382ec66b Mon Sep 17 00:00:00 2001 From: sonsumin Date: Wed, 20 Nov 2024 20:07:02 +0900 Subject: [PATCH 60/73] =?UTF-8?q?[#1]=E2=9C=85Test:=20=EB=B0=B0=ED=8F=AC?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/build.gradle b/build.gradle index eb600aa..7a21992 100644 --- a/build.gradle +++ b/build.gradle @@ -59,11 +59,11 @@ tasks.named("jar") { enabled = false } -tasks.register('copyExternalProperties', Copy) { - from "./config" - include "*.yml" - into "src/main/resources" -} +//tasks.register('copyExternalProperties', Copy) { +// from "./config" +// include "*.yml" +// into "src/main/resources" +//} processResources.dependsOn('copyExternalProperties') processTestResources.dependsOn('copyExternalProperties') From c19587384332351188f07b6fe550d0ae70630d70 Mon Sep 17 00:00:00 2001 From: sonsumin Date: Wed, 20 Nov 2024 20:13:17 +0900 Subject: [PATCH 61/73] =?UTF-8?q?[#1]=E2=9C=85Test:=20=EB=B0=B0=ED=8F=AC?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/build.gradle b/build.gradle index 7a21992..eb600aa 100644 --- a/build.gradle +++ b/build.gradle @@ -59,11 +59,11 @@ tasks.named("jar") { enabled = false } -//tasks.register('copyExternalProperties', Copy) { -// from "./config" -// include "*.yml" -// into "src/main/resources" -//} +tasks.register('copyExternalProperties', Copy) { + from "./config" + include "*.yml" + into "src/main/resources" +} processResources.dependsOn('copyExternalProperties') processTestResources.dependsOn('copyExternalProperties') From 2e7973e1bdaf75a57d5fb578430aa22183da739a Mon Sep 17 00:00:00 2001 From: sonsumin Date: Wed, 20 Nov 2024 20:23:33 +0900 Subject: [PATCH 62/73] =?UTF-8?q?[#1]=E2=9C=85Test:=20=EB=B0=B0=ED=8F=AC?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitmodules | 3 +++ config | 1 + 2 files changed, 4 insertions(+) create mode 100644 .gitmodules create mode 160000 config diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..15d2e43 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "config"] + path = config + url = https://github.com/YE-Summit/config.git diff --git a/config b/config new file mode 160000 index 0000000..f5d5cec --- /dev/null +++ b/config @@ -0,0 +1 @@ +Subproject commit f5d5cec3783fafb046235031e7d6de83b593ccf8 From 3646018aec6c42f98e178aa468ee1aa2e24143c2 Mon Sep 17 00:00:00 2001 From: sonsumin Date: Wed, 20 Nov 2024 20:29:45 +0900 Subject: [PATCH 63/73] =?UTF-8?q?[#1]=E2=9C=85Test:=20=EB=B0=B0=ED=8F=AC?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config b/config index f5d5cec..3c7d466 160000 --- a/config +++ b/config @@ -1 +1 @@ -Subproject commit f5d5cec3783fafb046235031e7d6de83b593ccf8 +Subproject commit 3c7d4665d23e71efdf348bda145d991311a6ff3a From 29a6c512e1e03ded981444bace4ac41055bd62a6 Mon Sep 17 00:00:00 2001 From: sonsumin Date: Wed, 20 Nov 2024 22:08:39 +0900 Subject: [PATCH 64/73] =?UTF-8?q?[#1]=F0=9F=92=9AChore:=20=EB=B8=8C?= =?UTF-8?q?=EB=9E=9C=EC=B9=98=20=EC=88=98=EC=A0=95=20dev=20->=20main?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy-prod.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy-prod.yml b/.github/workflows/deploy-prod.yml index db8de38..e4e1824 100644 --- a/.github/workflows/deploy-prod.yml +++ b/.github/workflows/deploy-prod.yml @@ -14,9 +14,9 @@ name: Backend CI & CD (dev) on: pull_request: - branches: [dev] + branches: [main] push: - branches: [dev] + branches: [main] env: CONTAINER_NAME: yesummit From e2182d2dbb801b6e28938d6f77bf378bbb419735 Mon Sep 17 00:00:00 2001 From: sonsumin Date: Wed, 20 Nov 2024 23:31:09 +0900 Subject: [PATCH 65/73] =?UTF-8?q?[#1]=E2=9C=A8Chore:=20S3=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config b/config index 3c7d466..a9020d7 160000 --- a/config +++ b/config @@ -1 +1 @@ -Subproject commit 3c7d4665d23e71efdf348bda145d991311a6ff3a +Subproject commit a9020d7b5d42fa369b56e82c8ac4c67d8974b299 From fe9f7f8c95ba532d9309bd80134d218c3d31092c Mon Sep 17 00:00:00 2001 From: sonsumin Date: Wed, 20 Nov 2024 23:32:17 +0900 Subject: [PATCH 66/73] =?UTF-8?q?[#6]=E2=9C=A8Feat:=20Feed=20=EC=97=94?= =?UTF-8?q?=ED=8B=B0=ED=8B=B0=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yesummit/domain/feed/entity/Feed.java | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 src/main/java/univ/yesummit/domain/feed/entity/Feed.java diff --git a/src/main/java/univ/yesummit/domain/feed/entity/Feed.java b/src/main/java/univ/yesummit/domain/feed/entity/Feed.java new file mode 100644 index 0000000..bc4a224 --- /dev/null +++ b/src/main/java/univ/yesummit/domain/feed/entity/Feed.java @@ -0,0 +1,31 @@ +package univ.yesummit.domain.feed.entity; + +import jakarta.persistence.*; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import univ.yesummit.domain.member.entity.Member; + +@Entity +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class Feed { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "feed_id") + private Long id; + + @Column(name = "feed_image") + private String image; + + @ManyToOne + @JoinColumn(name = "member_id") + private Member member; + + @Builder + public Feed(String image, Member member) { + this.image = image; + this.member = member; + } +} From ba349de5146e3b642a0326ce70c05dd71ec3be33 Mon Sep 17 00:00:00 2001 From: sonsumin Date: Wed, 20 Nov 2024 23:32:33 +0900 Subject: [PATCH 67/73] =?UTF-8?q?[#6]=E2=9C=A8Feat:=20Feed=20=EC=97=94?= =?UTF-8?q?=EB=93=9C=ED=8F=AC=EC=9D=B8=ED=8A=B8=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../feed/controller/FeedController.java | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 src/main/java/univ/yesummit/domain/feed/controller/FeedController.java diff --git a/src/main/java/univ/yesummit/domain/feed/controller/FeedController.java b/src/main/java/univ/yesummit/domain/feed/controller/FeedController.java new file mode 100644 index 0000000..cece9fe --- /dev/null +++ b/src/main/java/univ/yesummit/domain/feed/controller/FeedController.java @@ -0,0 +1,55 @@ +package univ.yesummit.domain.feed.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.http.MediaType; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; +import univ.yesummit.domain.feed.dto.FeedListResponse; +import univ.yesummit.domain.feed.dto.FeedResponse; +import univ.yesummit.domain.feed.service.FeedService; +import univ.yesummit.global.exception.dto.ResponseVO; +import univ.yesummit.global.resolver.LoginUser; +import univ.yesummit.global.resolver.User; + +import java.util.List; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/v1/api/feed") +public class FeedController { + + private final FeedService feedService; + + + @Operation(summary = "S3 버킷에 이미지를 업로드합니다.", description = "이미지 업로드 완료 후, 각각 이미지의 URL LIST를 JSON 형식으로 반환합니다.") + @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseVO uploadImage( + @Parameter(description = "multipart/form-data 형식의 이미지 리스트를 input으로 받습니다. 이때 key 값은 files입니다.") + @RequestParam("files") List files, + @User LoginUser loginUser) { + Long memberId = loginUser.getMemberId(); + + List imageUrls = feedService.uploadImagesToS3(files, memberId); + return new ResponseVO<>(new FeedResponse(imageUrls)); + } + + @GetMapping + @Operation(summary = "사용자의 모든 피드 조회", description = "사용자의 모든 피드를 조회하고 해당 리스트를 반환합니다.") + public ResponseVO getAllFeeds(@User LoginUser loginUser) { + Long memberId = loginUser.getMemberId(); + FeedListResponse feedListResponse = feedService.getAllFeedsOfMember(memberId); + return new ResponseVO<>(feedListResponse); + } + + @DeleteMapping("/{feedId}") + @Operation(summary = "피드 및 이미지 삭제", description = "피드와 해당 이미지를 삭제합니다.") + public ResponseVO deleteFeed(@PathVariable Long feedId, @User LoginUser loginUser) { + Long memberId = loginUser.getMemberId(); + feedService.deleteFeed(feedId, memberId); + return new ResponseVO<>("피드가 성공적으로 삭제되었습니다."); + } +} From cc0d5761c37c281142e28c361a7296c470326de9 Mon Sep 17 00:00:00 2001 From: sonsumin Date: Wed, 20 Nov 2024 23:32:45 +0900 Subject: [PATCH 68/73] =?UTF-8?q?[#6]=E2=9C=A8Feat:=20FeedListResponse=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../univ/yesummit/domain/feed/dto/FeedListResponse.java | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 src/main/java/univ/yesummit/domain/feed/dto/FeedListResponse.java diff --git a/src/main/java/univ/yesummit/domain/feed/dto/FeedListResponse.java b/src/main/java/univ/yesummit/domain/feed/dto/FeedListResponse.java new file mode 100644 index 0000000..05da15a --- /dev/null +++ b/src/main/java/univ/yesummit/domain/feed/dto/FeedListResponse.java @@ -0,0 +1,8 @@ +package univ.yesummit.domain.feed.dto; + +import java.util.List; + +public record FeedListResponse( + List feeds +) { +} From 3f62cd6657eaed1b90036196b5a304bdd3777570 Mon Sep 17 00:00:00 2001 From: sonsumin Date: Wed, 20 Nov 2024 23:32:59 +0900 Subject: [PATCH 69/73] =?UTF-8?q?[#6]=E2=9C=A8Feat:=20FeedRepository=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/feed/repository/FeedRepository.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 src/main/java/univ/yesummit/domain/feed/repository/FeedRepository.java diff --git a/src/main/java/univ/yesummit/domain/feed/repository/FeedRepository.java b/src/main/java/univ/yesummit/domain/feed/repository/FeedRepository.java new file mode 100644 index 0000000..763dda3 --- /dev/null +++ b/src/main/java/univ/yesummit/domain/feed/repository/FeedRepository.java @@ -0,0 +1,14 @@ +package univ.yesummit.domain.feed.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import univ.yesummit.domain.feed.entity.Feed; +import univ.yesummit.domain.member.entity.Member; + +import java.util.List; + +@Repository +public interface FeedRepository extends JpaRepository { + + List findAllByMember(Member member); +} From ebb8440b201396c7ef3e1ddae3f3c124e9a92c84 Mon Sep 17 00:00:00 2001 From: sonsumin Date: Wed, 20 Nov 2024 23:33:10 +0900 Subject: [PATCH 70/73] =?UTF-8?q?[#6]=E2=9C=A8Feat:=20FeedResponse=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/univ/yesummit/domain/feed/dto/FeedResponse.java | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 src/main/java/univ/yesummit/domain/feed/dto/FeedResponse.java diff --git a/src/main/java/univ/yesummit/domain/feed/dto/FeedResponse.java b/src/main/java/univ/yesummit/domain/feed/dto/FeedResponse.java new file mode 100644 index 0000000..3a6c0eb --- /dev/null +++ b/src/main/java/univ/yesummit/domain/feed/dto/FeedResponse.java @@ -0,0 +1,8 @@ +package univ.yesummit.domain.feed.dto; + +import java.util.List; + +public record FeedResponse( + List imageUrls +) { +} From 8317f18042c5f12b95cd6c98454d7d60975eb61a Mon Sep 17 00:00:00 2001 From: sonsumin Date: Wed, 20 Nov 2024 23:33:26 +0900 Subject: [PATCH 71/73] =?UTF-8?q?[#6]=E2=9C=A8Feat:=20FeedService=20?= =?UTF-8?q?=EC=9D=B8=ED=84=B0=ED=8E=98=EC=9D=B4=EC=8A=A4=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/feed/service/FeedService.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 src/main/java/univ/yesummit/domain/feed/service/FeedService.java diff --git a/src/main/java/univ/yesummit/domain/feed/service/FeedService.java b/src/main/java/univ/yesummit/domain/feed/service/FeedService.java new file mode 100644 index 0000000..80ee497 --- /dev/null +++ b/src/main/java/univ/yesummit/domain/feed/service/FeedService.java @@ -0,0 +1,16 @@ +package univ.yesummit.domain.feed.service; + +import org.springframework.web.multipart.MultipartFile; +import univ.yesummit.domain.feed.dto.FeedListResponse; +import univ.yesummit.domain.member.entity.Member; + +import java.util.List; + +public interface FeedService { + + List uploadImagesToS3(List files, Long memberId); + + FeedListResponse getAllFeedsOfMember(Long memberId); + + void deleteFeed(Long feedId, Long memberId); +} From 370b607887f2fb3dd7470ee19cdea649855ea677 Mon Sep 17 00:00:00 2001 From: sonsumin Date: Wed, 20 Nov 2024 23:33:36 +0900 Subject: [PATCH 72/73] =?UTF-8?q?[#6]=E2=9C=A8Feat:=20FeedService=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=EC=B2=B4=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../feed/service/impl/FeedServiceImpl.java | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 src/main/java/univ/yesummit/domain/feed/service/impl/FeedServiceImpl.java diff --git a/src/main/java/univ/yesummit/domain/feed/service/impl/FeedServiceImpl.java b/src/main/java/univ/yesummit/domain/feed/service/impl/FeedServiceImpl.java new file mode 100644 index 0000000..54da692 --- /dev/null +++ b/src/main/java/univ/yesummit/domain/feed/service/impl/FeedServiceImpl.java @@ -0,0 +1,81 @@ +package univ.yesummit.domain.feed.service.impl; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; +import univ.yesummit.domain.feed.dto.FeedListResponse; +import univ.yesummit.domain.feed.dto.FeedResponse; +import univ.yesummit.domain.feed.entity.Feed; +import univ.yesummit.domain.feed.repository.FeedRepository; +import univ.yesummit.domain.feed.service.FeedService; +import univ.yesummit.domain.member.entity.Member; +import univ.yesummit.domain.member.exception.MemberException; +import univ.yesummit.domain.member.repository.MemberRepository; +import univ.yesummit.global.exception.ErrorCode; +import univ.yesummit.global.s3.service.S3Service; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + + +@Service +@RequiredArgsConstructor +@Slf4j +public class FeedServiceImpl implements FeedService { + + + private final FeedRepository feedRepository; + private final MemberRepository memberRepository; + private final S3Service s3Service; + + @Override + public List uploadImagesToS3(List files, Long memberId) { + Member member = memberRepository.findById(memberId).orElseThrow(() -> new MemberException(ErrorCode.NOT_FOUND_MEMBER)); + List imageUrls = new ArrayList<>(); + for (MultipartFile file : files) { + String imageUrl = s3Service.uploadImage(file, "feed-images"); + imageUrls.add(imageUrl); + + Feed feed = Feed.builder() + .image(imageUrl) + .member(member) + .build(); + feedRepository.save(feed); + } + return imageUrls; + } + + @Override + public FeedListResponse getAllFeedsOfMember(Long memberId) { + Member member = memberRepository.findById(memberId).orElseThrow(() -> new MemberException(ErrorCode.NOT_FOUND_MEMBER)); + List feeds = feedRepository.findAllByMember(member); + List feedResponses = feeds.stream() + .map(feed -> new FeedResponse(Collections.singletonList(feed.getImage()))) + .collect(Collectors.toList()); + return new FeedListResponse(feedResponses); + } + + @Override + public void deleteFeed(Long feedId, Long memberId) { + Member member = memberRepository.findById(memberId).orElseThrow(() -> new MemberException(ErrorCode.NOT_FOUND_MEMBER)); + Feed feed = feedRepository.findById(feedId) + .orElseThrow(() -> new RuntimeException("피드를 찾을 수 없습니다.")); + + // 피드가 해당 회원의 것인지 확인 + if (!feed.getMember().getId().equals(member.getId())) { + throw new RuntimeException("삭제 권한이 없습니다."); + } + + // S3에서 이미지 삭제 + s3Service.deleteImage(feed.getImage()); + + // 데이터베이스에서 피드 삭제 + feedRepository.delete(feed); + } + +} + + From f125ca8ec6d81b86a090efb9303d5c996f7efcc3 Mon Sep 17 00:00:00 2001 From: sonsumin Date: Wed, 20 Nov 2024 23:35:10 +0900 Subject: [PATCH 73/73] =?UTF-8?q?[#3]=E2=99=BB=EF=B8=8FFeat:=20LoginUser?= =?UTF-8?q?=EC=9D=98=20memberId=EB=A1=9C=20=EA=B0=80=EC=A0=B8=EC=98=A4?= =?UTF-8?q?=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/member/controller/MemberController.java | 7 ++++--- .../univ/yesummit/domain/member/service/MemberService.java | 4 ++-- .../domain/member/service/impl/MemberServiceImpl.java | 4 ++-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/main/java/univ/yesummit/domain/member/controller/MemberController.java b/src/main/java/univ/yesummit/domain/member/controller/MemberController.java index eedb86c..973e3a3 100644 --- a/src/main/java/univ/yesummit/domain/member/controller/MemberController.java +++ b/src/main/java/univ/yesummit/domain/member/controller/MemberController.java @@ -31,7 +31,7 @@ public class MemberController { @PostMapping("/saveAdditionalInfo") public void saveAdditionalInfo(@User LoginUser loginUser, @Valid @RequestBody MemberSignUpDTO memberSignUpDTO) throws Exception { Long memberId = loginUser.getMemberId(); - memberService.saveAdditionalInfo(memberSignUpDTO, memberId); + memberService.saveAdditionalInfo(memberId, memberSignUpDTO); } /** @@ -40,8 +40,9 @@ public void saveAdditionalInfo(@User LoginUser loginUser, @Valid @RequestBody Me @PutMapping @Operation(summary = "회원 정보 수정", description = "회원 정보를 수정합니다.") @ResponseStatus(HttpStatus.OK) - public void updateInfo(@Valid @RequestBody MemberUpdateDTO memberUpdateDTO) throws Exception { - memberService.updateInfo(memberUpdateDTO); + public void updateInfo(@User LoginUser loginUser, @Valid @RequestBody MemberUpdateDTO memberUpdateDTO) throws Exception { + Long memberId = loginUser.getMemberId(); + memberService.updateInfo(memberId, memberUpdateDTO); } /** diff --git a/src/main/java/univ/yesummit/domain/member/service/MemberService.java b/src/main/java/univ/yesummit/domain/member/service/MemberService.java index 0100d26..eeb40d9 100644 --- a/src/main/java/univ/yesummit/domain/member/service/MemberService.java +++ b/src/main/java/univ/yesummit/domain/member/service/MemberService.java @@ -11,7 +11,7 @@ public interface MemberService { /** * 추가 정보 수집 */ - void saveAdditionalInfo(MemberSignUpDTO dto, Long memberId) throws Exception; + void saveAdditionalInfo(Long memberId, MemberSignUpDTO dto) throws Exception; /** * 첫 로그인 확인 @@ -26,7 +26,7 @@ public interface MemberService { /** * 회원 정보 수정 */ - void updateInfo(MemberUpdateDTO dto) throws Exception; + void updateInfo(Long memberId, MemberUpdateDTO dto) throws Exception; /** * 내 정보 조회 diff --git a/src/main/java/univ/yesummit/domain/member/service/impl/MemberServiceImpl.java b/src/main/java/univ/yesummit/domain/member/service/impl/MemberServiceImpl.java index 92fa9b6..bd4a109 100644 --- a/src/main/java/univ/yesummit/domain/member/service/impl/MemberServiceImpl.java +++ b/src/main/java/univ/yesummit/domain/member/service/impl/MemberServiceImpl.java @@ -22,7 +22,7 @@ public class MemberServiceImpl implements MemberService { private final MemberRepository memberRepository; @Override - public void saveAdditionalInfo(MemberSignUpDTO memberSignUpDTO, Long memberId) throws Exception { + public void saveAdditionalInfo(Long memberId, MemberSignUpDTO memberSignUpDTO) throws Exception { if (memberId == null) { throw new IllegalArgumentException("회원 ID가 null입니다. 인증 정보를 확인하세요."); @@ -68,7 +68,7 @@ public void withdraw(Long memberId, MemberWithdrawDTO memberWithdrawDTO) throws } @Override - public void updateInfo(MemberUpdateDTO memberUpdateDTO) throws Exception { + public void updateInfo(Long memberId, MemberUpdateDTO memberUpdateDTO) throws Exception { Member member = memberRepository.getByUsernameOrThrow(SecurityUtil.getLoginUsername());