diff --git a/second-seminar/second-seminar/.gitignore b/second-seminar/second-seminar/.gitignore index c2065bc..42de9f7 100644 --- a/second-seminar/second-seminar/.gitignore +++ b/second-seminar/second-seminar/.gitignore @@ -35,3 +35,10 @@ out/ ### VS Code ### .vscode/ + +*.yml +application-local.yml +*.sql +src/main/resources/application-local.yml +src/main/resources/application-dev.yml +src/main/resources/data.sql \ No newline at end of file diff --git a/second-seminar/second-seminar/build.gradle b/second-seminar/second-seminar/build.gradle index 92a35cf..b6866ce 100644 --- a/second-seminar/second-seminar/build.gradle +++ b/second-seminar/second-seminar/build.gradle @@ -36,6 +36,18 @@ dependencies { // security implementation 'org.springframework.boot:spring-boot-starter-security' testImplementation 'org.springframework.security:spring-security-test' + + dependencies { + // AWS sdk + implementation("software.amazon.awssdk:bom:2.21.0") + implementation("software.amazon.awssdk:s3:2.21.0") + } + + dependencies { + implementation group: 'io.jsonwebtoken', name: 'jjwt-api', version: '0.11.5' + implementation group: 'io.jsonwebtoken', name: 'jjwt-impl', version: '0.11.5' + implementation group: 'io.jsonwebtoken', name: 'jjwt-jackson', version: '0.11.5' + } } tasks.named('test') { diff --git a/second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/SecondSeminarApplication.java b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/SecondSeminarApplication.java similarity index 88% rename from second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/SecondSeminarApplication.java rename to second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/SecondSeminarApplication.java index 9ffcc86..5901ddb 100644 --- a/second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/SecondSeminarApplication.java +++ b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/SecondSeminarApplication.java @@ -1,4 +1,4 @@ -package thirdseminar.thirdseminar; +package secondSeminar.secondSeminar; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; diff --git a/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/config/AwsConfig.java b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/config/AwsConfig.java new file mode 100644 index 0000000..758a1fe --- /dev/null +++ b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/config/AwsConfig.java @@ -0,0 +1,57 @@ +package secondSeminar.secondSeminar.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import software.amazon.awssdk.auth.credentials.SystemPropertyCredentialsProvider; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.s3.S3Client; +import software.amazon.awssdk.services.s3.presigner.S3Presigner; + +@Configuration +public class AwsConfig { + + private static final String AWS_ACCESS_KEY_ID = "aws.accessKeyId"; + private static final String AWS_SECRET_ACCESS_KEY = "aws.secretAccessKey"; + + private final String accessKey; + private final String secretKey; + private final String regionString; + + public AwsConfig(@Value("${aws-property.access-key}") final String accessKey, + @Value("${aws-property.secret-key}") final String secretKey, + @Value("${aws-property.aws-region}") final String regionString) { + this.accessKey = accessKey; + this.secretKey = secretKey; + this.regionString = regionString; + } + + + @Bean + public SystemPropertyCredentialsProvider systemPropertyCredentialsProvider() { + System.setProperty(AWS_ACCESS_KEY_ID, accessKey); + System.setProperty(AWS_SECRET_ACCESS_KEY, secretKey); + return SystemPropertyCredentialsProvider.create(); + } + + @Bean + public Region getRegion() { + return Region.of(regionString); + } + + @Bean + public S3Client getS3Client() { + return S3Client.builder() + .region(getRegion()) + .credentialsProvider(systemPropertyCredentialsProvider()) + .build(); + } + + @Bean + S3Presigner getS3Presigner() { + return S3Presigner.builder() + .region(getRegion()) + .credentialsProvider(systemPropertyCredentialsProvider()) + .build(); + } +} diff --git a/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/config/BCryptPasswordConfig.java b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/config/BCryptPasswordConfig.java new file mode 100644 index 0000000..720f70d --- /dev/null +++ b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/config/BCryptPasswordConfig.java @@ -0,0 +1,17 @@ +package secondSeminar.secondSeminar.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; + +@Configuration +public class BCryptPasswordConfig { + + private static final int STRENGTH = 10; + + @Bean + public PasswordEncoder bCryptPasswordEncoder() { + return new BCryptPasswordEncoder(STRENGTH); + } +} diff --git a/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/config/CustomAccessDeniedHandler.java b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/config/CustomAccessDeniedHandler.java new file mode 100644 index 0000000..211b6db --- /dev/null +++ b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/config/CustomAccessDeniedHandler.java @@ -0,0 +1,21 @@ +package secondSeminar.secondSeminar.config; + +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.web.access.AccessDeniedHandler; +import org.springframework.stereotype.Component; + +@Component +public class CustomAccessDeniedHandler implements AccessDeniedHandler { + @Override + public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException { + setResponse(response); + } + + private void setResponse(HttpServletResponse response) { + response.setStatus(HttpServletResponse.SC_FORBIDDEN); + } +} diff --git a/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/config/CustomJwtAuthenticationEntryPoint.java b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/config/CustomJwtAuthenticationEntryPoint.java new file mode 100644 index 0000000..44c0132 --- /dev/null +++ b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/config/CustomJwtAuthenticationEntryPoint.java @@ -0,0 +1,20 @@ +package secondSeminar.secondSeminar.config; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.stereotype.Component; + +@Component +public class CustomJwtAuthenticationEntryPoint implements AuthenticationEntryPoint { + + @Override + public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) { + setResponse(response); + } + + private void setResponse(HttpServletResponse response) { + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + } +} diff --git a/second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/config/JpaAuditingConfig.java b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/config/JpaAuditingConfig.java similarity index 82% rename from second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/config/JpaAuditingConfig.java rename to second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/config/JpaAuditingConfig.java index 4797a98..7892f17 100644 --- a/second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/config/JpaAuditingConfig.java +++ b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/config/JpaAuditingConfig.java @@ -1,4 +1,4 @@ -package thirdseminar.thirdseminar.config; +package secondSeminar.secondSeminar.config; import org.springframework.context.annotation.Configuration; import org.springframework.data.jpa.repository.config.EnableJpaAuditing; diff --git a/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/config/JwtAuthenticationFilter.java b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/config/JwtAuthenticationFilter.java new file mode 100644 index 0000000..771f232 --- /dev/null +++ b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/config/JwtAuthenticationFilter.java @@ -0,0 +1,56 @@ +package secondSeminar.secondSeminar.config; + + +import static secondSeminar.secondSeminar.config.JwtValidationType.VALID_JWT; + +import io.micrometer.common.lang.NonNull; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; +import lombok.RequiredArgsConstructor; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; +import org.springframework.web.filter.OncePerRequestFilter; + +@Component +@RequiredArgsConstructor +public class JwtAuthenticationFilter extends OncePerRequestFilter { + + private final JwtTokenProvider jwtTokenProvider; + + @Override + protected void doFilterInternal(@NonNull HttpServletRequest request, + @NonNull HttpServletResponse response, + @NonNull FilterChain filterChain) throws ServletException, IOException { + try { + final String token = getJwtFromRequest(request); + if (jwtTokenProvider.validateToken(token) == VALID_JWT) { + Long memberId = jwtTokenProvider.getUserFromJwt(token); + // authentication 객체 생성 -> principal에 유저정보를 담는다. + UserAuthentication authentication = new UserAuthentication(memberId.toString(), null, null); + authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); + SecurityContextHolder.getContext().setAuthentication(authentication); + } + } catch (Exception exception) { + try { + throw new Exception(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + // 다음 필터로 요청 전달 + filterChain.doFilter(request, response); + } + + private String getJwtFromRequest(HttpServletRequest request) { + String bearerToken = request.getHeader("Authorization"); + if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) { + return bearerToken.substring("Bearer ".length()); + } + return null; + } +} diff --git a/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/config/JwtTokenProvider.java b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/config/JwtTokenProvider.java new file mode 100644 index 0000000..c52ab99 --- /dev/null +++ b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/config/JwtTokenProvider.java @@ -0,0 +1,84 @@ +package secondSeminar.secondSeminar.config; + +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.ExpiredJwtException; +import io.jsonwebtoken.Header; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.MalformedJwtException; +import io.jsonwebtoken.UnsupportedJwtException; +import io.jsonwebtoken.security.Keys; +import jakarta.annotation.PostConstruct; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.core.Authentication; +import org.springframework.stereotype.Component; + +import javax.crypto.SecretKey; +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.Date; + +@Component +@RequiredArgsConstructor +public class JwtTokenProvider { + + private static final String MEMBER_ID = "memberId"; + + @Value("${jwt.secret}") + private String JWT_SECRET; + + @PostConstruct + protected void init() { + //base64 라이브러리에서 encodeToString을 이용해서 byte[] 형식을 String 형식으로 변환 + JWT_SECRET = Base64.getEncoder().encodeToString(JWT_SECRET.getBytes(StandardCharsets.UTF_8)); + } + + public String generateToken(Authentication authentication, Long tokenExpirationTime) { + final Date now = new Date(); + final Claims claims = Jwts.claims() + .setIssuedAt(now) + .setExpiration(new Date(now.getTime() + tokenExpirationTime)); // 만료 시간 + + claims.put(MEMBER_ID, authentication.getPrincipal()); + + return Jwts.builder() + .setHeaderParam(Header.TYPE, Header.JWT_TYPE) // Header + .setClaims(claims) // Claim + .signWith(getSigningKey()) // Signature + .compact(); + } + + private SecretKey getSigningKey() { + String encodedKey = Base64.getEncoder().encodeToString(JWT_SECRET.getBytes()); //SecretKey 통해 서명 생성 + return Keys.hmacShaKeyFor( + encodedKey.getBytes()); //일반적으로 HMAC (Hash-based Message Authentication Code) 알고리즘 사용 + } + + public JwtValidationType validateToken(String token) { + try { + final Claims claims = getBody(token); + return JwtValidationType.VALID_JWT; + } catch (MalformedJwtException ex) { + return JwtValidationType.INVALID_JWT_TOKEN; + } catch (ExpiredJwtException ex) { + return JwtValidationType.EXPIRED_JWT_TOKEN; + } catch (UnsupportedJwtException ex) { + return JwtValidationType.UNSUPPORTED_JWT_TOKEN; + } catch (IllegalArgumentException ex) { + return JwtValidationType.EMPTY_JWT; + } + } + + private Claims getBody(final String token) { + return Jwts.parserBuilder() + .setSigningKey(getSigningKey()) + .build() + .parseClaimsJws(token) + .getBody(); + } + + public Long getUserFromJwt(String token) { + Claims claims = getBody(token); + return Long.valueOf(claims.get(MEMBER_ID).toString()); + } +} diff --git a/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/config/JwtValidationType.java b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/config/JwtValidationType.java new file mode 100644 index 0000000..65e54c4 --- /dev/null +++ b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/config/JwtValidationType.java @@ -0,0 +1,10 @@ +package secondSeminar.secondSeminar.config; + +public enum JwtValidationType { + VALID_JWT, // 유효한 JWT + INVALID_JWT_SIGNATURE, // 유효하지 않은 서명 + INVALID_JWT_TOKEN, // 유효하지 않은 토큰 + EXPIRED_JWT_TOKEN, // 만료된 토큰 + UNSUPPORTED_JWT_TOKEN, // 지원하지 않는 형식의 토큰 + EMPTY_JWT // 빈 JWT +} diff --git a/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/config/SecurityConfig.java b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/config/SecurityConfig.java new file mode 100644 index 0000000..4a6fe37 --- /dev/null +++ b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/config/SecurityConfig.java @@ -0,0 +1,63 @@ +package secondSeminar.secondSeminar.config; + +import static org.springframework.security.config.http.SessionCreationPolicy.STATELESS; + +import jakarta.servlet.Filter; +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +@EnableWebSecurity +@RequiredArgsConstructor +public class SecurityConfig { + + private final JwtAuthenticationFilter jwtAuthenticationFilter; + private final CustomJwtAuthenticationEntryPoint customJwtAuthenticationEntryPoint; + private final CustomAccessDeniedHandler customAccessDeniedHandler; + + + private static final String[] AUTH_WHITELIST = { + "/sign-up", + "/sign-in" + }; + @Bean + SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + return http + .csrf().disable() + .formLogin().disable() + .httpBasic().disable() + .sessionManagement() + .sessionCreationPolicy(STATELESS) + .and() + .exceptionHandling() + .authenticationEntryPoint(customJwtAuthenticationEntryPoint) + .accessDeniedHandler(customAccessDeniedHandler) + .and() + .authorizeHttpRequests() + .requestMatchers(AUTH_WHITELIST).permitAll() + .anyRequest().authenticated() + .and() + .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class) + .build(); + } + + @Bean + public WebMvcConfigurer corsConfigurer() { + return new WebMvcConfigurer() { + @Override + public void addCorsMappings(CorsRegistry registry) { + registry.addMapping("/**") + .allowedOrigins("*") + .allowedOriginPatterns("*") + .allowedMethods("*"); + } + }; + } +} diff --git a/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/config/UserAuthentication.java b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/config/UserAuthentication.java new file mode 100644 index 0000000..ec149e6 --- /dev/null +++ b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/config/UserAuthentication.java @@ -0,0 +1,13 @@ +package secondSeminar.secondSeminar.config; + +import java.util.Collection; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.GrantedAuthority; + +public class UserAuthentication extends UsernamePasswordAuthenticationToken { + + // 사용자 인증 객체 생성 + public UserAuthentication(Object principal, Object credentials, Collection authorities) { + super(principal, credentials, authorities); + } +} diff --git a/second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/controller/MemberController.java b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/controller/MemberController.java similarity index 88% rename from second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/controller/MemberController.java rename to second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/controller/MemberController.java index 98e6604..635493b 100644 --- a/second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/controller/MemberController.java +++ b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/controller/MemberController.java @@ -1,4 +1,4 @@ -package thirdseminar.thirdseminar.controller; +package secondSeminar.secondSeminar.controller; import java.net.URI; import java.util.List; @@ -14,10 +14,10 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import thirdseminar.thirdseminar.dto.request.member.MemberCreateRequest; -import thirdseminar.thirdseminar.dto.request.member.MemberProfileUpdateRequest; -import thirdseminar.thirdseminar.dto.response.MemberGetResponse; -import thirdseminar.thirdseminar.service.MemberService; +import secondSeminar.secondSeminar.dto.request.member.MemberCreateRequest; +import secondSeminar.secondSeminar.dto.request.member.MemberProfileUpdateRequest; +import secondSeminar.secondSeminar.dto.response.MemberGetResponse; +import secondSeminar.secondSeminar.service.MemberService; @RestController diff --git a/second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/controller/PostController.java b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/controller/PostController.java similarity index 68% rename from second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/controller/PostController.java rename to second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/controller/PostController.java index c124499..e791ef4 100644 --- a/second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/controller/PostController.java +++ b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/controller/PostController.java @@ -1,6 +1,7 @@ -package thirdseminar.thirdseminar.controller; +package secondSeminar.secondSeminar.controller; import java.net.URI; +import java.security.Principal; import java.util.List; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -12,9 +13,9 @@ import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import thirdseminar.thirdseminar.dto.request.post.PostCreateRequest; -import thirdseminar.thirdseminar.dto.request.post.PostGetResponse; -import thirdseminar.thirdseminar.service.PostService; +import secondSeminar.secondSeminar.dto.request.post.PostCreateRequest; +import secondSeminar.secondSeminar.dto.request.post.PostGetResponse; +import secondSeminar.secondSeminar.service.PostService; @RestController @RequiredArgsConstructor @@ -26,10 +27,11 @@ public class PostController { private final PostService postService; @PostMapping - public ResponseEntity createPost(@RequestHeader(CUSTOM_AUTH_ID) Long memberId, - @RequestBody PostCreateRequest request){ - String postId = postService.create(request, memberId); - URI location = URI.create("/api/posts/" + postId); + public ResponseEntity createPost(@RequestBody PostCreateRequest request, + Principal principal){ + Long memberId = Long.valueOf(principal.getName()); + URI location = URI.create("/api/posts/" + postService.create(request, memberId)); + return ResponseEntity.created(location).build(); } diff --git a/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/controller/PostControllerV2.java b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/controller/PostControllerV2.java new file mode 100644 index 0000000..f7d5885 --- /dev/null +++ b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/controller/PostControllerV2.java @@ -0,0 +1,38 @@ +package secondSeminar.secondSeminar.controller; + +import java.net.URI; +import java.security.Principal; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; +import secondSeminar.secondSeminar.dto.request.post.PostCreateRequest; +import secondSeminar.secondSeminar.service.PostServiceV2; + +@RestController +@RequestMapping("/api/v2/posts") +@RequiredArgsConstructor +public class PostControllerV2 { + + private final PostServiceV2 postService; + + @PostMapping + public ResponseEntity createPostV2(@RequestPart final MultipartFile image, + final PostCreateRequest request, + final Principal principal) { + URI location = URI.create("/api/posts/v2" + postService.createV2(request, image, Long.valueOf(principal.getName()))); + return ResponseEntity.created(location).build(); + } + + @DeleteMapping("{postId}") + public ResponseEntity deletePost(@PathVariable Long postId) { + postService.deleteByIdV2(postId); + return ResponseEntity.noContent().build(); + } +} \ No newline at end of file diff --git a/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/controller/ServiceMemberController.java b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/controller/ServiceMemberController.java new file mode 100644 index 0000000..8d921c1 --- /dev/null +++ b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/controller/ServiceMemberController.java @@ -0,0 +1,31 @@ +package secondSeminar.secondSeminar.controller; + +import java.net.URI; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import secondSeminar.secondSeminar.dto.request.servicemember.ServiceMemberRequest; +import secondSeminar.secondSeminar.service.ServiceMemberService; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/users/") +public class ServiceMemberController { + + private final ServiceMemberService serviceMemberService; + + @PostMapping("sign-up") + public ResponseEntity signUp(@RequestBody ServiceMemberRequest request) { + URI location = URI.create(serviceMemberService.create(request)); + return ResponseEntity.created(location).build(); + } + + @PostMapping("sign-in") + public ResponseEntity signIn(@RequestBody ServiceMemberRequest request) { + serviceMemberService.signIn(request); + return ResponseEntity.noContent().build(); + } +} diff --git a/second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/domain/BaseTimeEntity.java b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/domain/BaseTimeEntity.java similarity index 92% rename from second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/domain/BaseTimeEntity.java rename to second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/domain/BaseTimeEntity.java index c83df6d..70904a9 100644 --- a/second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/domain/BaseTimeEntity.java +++ b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/domain/BaseTimeEntity.java @@ -1,4 +1,4 @@ -package thirdseminar.thirdseminar.domain; +package secondSeminar.secondSeminar.domain; import jakarta.persistence.EntityListeners; import jakarta.persistence.MappedSuperclass; diff --git a/second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/domain/Category.java b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/domain/Category.java similarity index 89% rename from second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/domain/Category.java rename to second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/domain/Category.java index 356fd53..af410e8 100644 --- a/second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/domain/Category.java +++ b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/domain/Category.java @@ -1,4 +1,4 @@ -package thirdseminar.thirdseminar.domain; +package secondSeminar.secondSeminar.domain; import static lombok.AccessLevel.PROTECTED; diff --git a/second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/domain/CategoryId.java b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/domain/CategoryId.java similarity index 84% rename from second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/domain/CategoryId.java rename to second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/domain/CategoryId.java index 6170e40..0186809 100644 --- a/second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/domain/CategoryId.java +++ b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/domain/CategoryId.java @@ -1,4 +1,4 @@ -package thirdseminar.thirdseminar.domain; +package secondSeminar.secondSeminar.domain; import jakarta.persistence.Embeddable; import java.io.Serializable; diff --git a/second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/domain/Member.java b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/domain/Member.java similarity index 95% rename from second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/domain/Member.java rename to second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/domain/Member.java index 01752d8..56a0e33 100644 --- a/second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/domain/Member.java +++ b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/domain/Member.java @@ -1,4 +1,4 @@ -package thirdseminar.thirdseminar.domain; +package secondSeminar.secondSeminar.domain; import jakarta.persistence.Embedded; import jakarta.persistence.Entity; diff --git a/second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/domain/MemberId.java b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/domain/MemberId.java similarity index 84% rename from second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/domain/MemberId.java rename to second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/domain/MemberId.java index 56725b1..c75808c 100644 --- a/second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/domain/MemberId.java +++ b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/domain/MemberId.java @@ -1,4 +1,4 @@ -package thirdseminar.thirdseminar.domain; +package secondSeminar.secondSeminar.domain; import jakarta.persistence.Embeddable; import java.io.Serializable; diff --git a/second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/domain/Post.java b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/domain/Post.java similarity index 68% rename from second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/domain/Post.java rename to second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/domain/Post.java index 57effd2..cac6ef2 100644 --- a/second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/domain/Post.java +++ b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/domain/Post.java @@ -1,4 +1,4 @@ -package thirdseminar.thirdseminar.domain; +package secondSeminar.secondSeminar.domain; import jakarta.persistence.Column; import jakarta.persistence.Entity; @@ -21,18 +21,18 @@ public class Post extends BaseTimeEntity{ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long postId; + private Long id; private String title; - @Column(columnDefinition = "TEXT") private String content; + private String imageUrl; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "member_id") private Member member; @Column(name = "category_id") - private short categoryId; + private CategoryId categoryId; @Builder public Post(String title, String content, Member member) { @@ -41,6 +41,18 @@ public Post(String title, String content, Member member) { this.member = member; } + @Builder(builderMethodName = "builderWithImageUrl") + public Post(String title, String content, String imageUrl, Member member) { + this.title = title; + this.content = content; + this.imageUrl = imageUrl; + this.member = member; + } + + public void addCategory(CategoryId categoryId) { + this.categoryId = categoryId; + } + public void updateContent(String content) { this.content = content; } diff --git a/second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/domain/SOPT.java b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/domain/SOPT.java similarity index 84% rename from second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/domain/SOPT.java rename to second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/domain/SOPT.java index baeedd5..6e5dd25 100644 --- a/second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/domain/SOPT.java +++ b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/domain/SOPT.java @@ -1,4 +1,4 @@ -package thirdseminar.thirdseminar.domain; +package secondSeminar.secondSeminar.domain; import static jakarta.persistence.EnumType.STRING; @@ -8,7 +8,7 @@ import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; -import thirdseminar.thirdseminar.domain.enums.Part; +import secondSeminar.secondSeminar.domain.enums.Part; @Embeddable // @Embedded랑 세트! @Getter diff --git a/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/domain/ServiceMember.java b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/domain/ServiceMember.java new file mode 100644 index 0000000..9b7d674 --- /dev/null +++ b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/domain/ServiceMember.java @@ -0,0 +1,28 @@ +package secondSeminar.secondSeminar.domain; + +import jakarta.persistence.Entity; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import lombok.Builder; + +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Getter +public class ServiceMember { + + @Id + @GeneratedValue + private Long id; + + private String nickname; + private String password; + + @Builder + public ServiceMember(String nickname, String password) { + this.nickname = nickname; + this.password = password; + } +} diff --git a/second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/domain/Withdraw.java b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/domain/Withdraw.java similarity index 89% rename from second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/domain/Withdraw.java rename to second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/domain/Withdraw.java index 5fc64f7..f14aa4d 100644 --- a/second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/domain/Withdraw.java +++ b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/domain/Withdraw.java @@ -1,4 +1,4 @@ -package thirdseminar.thirdseminar.domain; +package secondSeminar.secondSeminar.domain; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; diff --git a/second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/domain/enums/Part.java b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/domain/enums/Part.java similarity index 84% rename from second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/domain/enums/Part.java rename to second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/domain/enums/Part.java index ce0a45a..c352c22 100644 --- a/second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/domain/enums/Part.java +++ b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/domain/enums/Part.java @@ -1,4 +1,4 @@ -package thirdseminar.thirdseminar.domain.enums; +package secondSeminar.secondSeminar.domain.enums; import lombok.AllArgsConstructor; import lombok.Getter; diff --git a/second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/dto/request/member/MemberCreateRequest.java b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/dto/request/member/MemberCreateRequest.java similarity index 50% rename from second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/dto/request/member/MemberCreateRequest.java rename to second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/dto/request/member/MemberCreateRequest.java index 041ebff..fcb315b 100644 --- a/second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/dto/request/member/MemberCreateRequest.java +++ b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/dto/request/member/MemberCreateRequest.java @@ -1,6 +1,6 @@ -package thirdseminar.thirdseminar.dto.request.member; +package secondSeminar.secondSeminar.dto.request.member; -import thirdseminar.thirdseminar.domain.SOPT; +import secondSeminar.secondSeminar.domain.SOPT; public record MemberCreateRequest ( String name, diff --git a/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/dto/request/member/MemberProfileUpdateRequest.java b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/dto/request/member/MemberProfileUpdateRequest.java new file mode 100644 index 0000000..8cdb558 --- /dev/null +++ b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/dto/request/member/MemberProfileUpdateRequest.java @@ -0,0 +1,9 @@ +package secondSeminar.secondSeminar.dto.request.member; + +import secondSeminar.secondSeminar.domain.enums.Part; + + +public record MemberProfileUpdateRequest ( + int generation, + Part part +){} diff --git a/second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/dto/request/post/PostCreateRequest.java b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/dto/request/post/PostCreateRequest.java similarity index 61% rename from second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/dto/request/post/PostCreateRequest.java rename to second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/dto/request/post/PostCreateRequest.java index 152c001..9a0e519 100644 --- a/second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/dto/request/post/PostCreateRequest.java +++ b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/dto/request/post/PostCreateRequest.java @@ -1,4 +1,4 @@ -package thirdseminar.thirdseminar.dto.request.post; +package secondSeminar.secondSeminar.dto.request.post; public record PostCreateRequest( String title, diff --git a/second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/dto/request/post/PostGetResponse.java b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/dto/request/post/PostGetResponse.java similarity index 64% rename from second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/dto/request/post/PostGetResponse.java rename to second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/dto/request/post/PostGetResponse.java index a789c46..36727be 100644 --- a/second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/dto/request/post/PostGetResponse.java +++ b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/dto/request/post/PostGetResponse.java @@ -1,7 +1,7 @@ -package thirdseminar.thirdseminar.dto.request.post; +package secondSeminar.secondSeminar.dto.request.post; -import thirdseminar.thirdseminar.domain.Category; -import thirdseminar.thirdseminar.domain.Post; +import secondSeminar.secondSeminar.domain.Category; +import secondSeminar.secondSeminar.domain.Post; public record PostGetResponse( Long id, @@ -12,7 +12,7 @@ public record PostGetResponse( public static PostGetResponse of(Post post, Category category) { return new PostGetResponse( - post.getPostId(), + post.getId(), post.getTitle(), post.getContent(), category.getContent() diff --git a/second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/dto/request/post/PostUpdateRequest.java b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/dto/request/post/PostUpdateRequest.java similarity index 53% rename from second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/dto/request/post/PostUpdateRequest.java rename to second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/dto/request/post/PostUpdateRequest.java index 9245015..459a90c 100644 --- a/second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/dto/request/post/PostUpdateRequest.java +++ b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/dto/request/post/PostUpdateRequest.java @@ -1,4 +1,4 @@ -package thirdseminar.thirdseminar.dto.request.post; +package secondSeminar.secondSeminar.dto.request.post; public record PostUpdateRequest( String content diff --git a/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/dto/request/servicemember/ServiceMemberRequest.java b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/dto/request/servicemember/ServiceMemberRequest.java new file mode 100644 index 0000000..1f51c0c --- /dev/null +++ b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/dto/request/servicemember/ServiceMemberRequest.java @@ -0,0 +1,7 @@ +package secondSeminar.secondSeminar.dto.request.servicemember; + +public record ServiceMemberRequest( + String nickname, + String password +) { +} diff --git a/second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/dto/response/CategoryResponse.java b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/dto/response/CategoryResponse.java similarity index 64% rename from second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/dto/response/CategoryResponse.java rename to second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/dto/response/CategoryResponse.java index 7dadee6..96c839e 100644 --- a/second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/dto/response/CategoryResponse.java +++ b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/dto/response/CategoryResponse.java @@ -1,6 +1,6 @@ -package thirdseminar.thirdseminar.dto.response; +package secondSeminar.secondSeminar.dto.response; -import thirdseminar.thirdseminar.domain.Category; +import secondSeminar.secondSeminar.domain.Category; public record CategoryResponse( String category diff --git a/second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/dto/response/MemberGetResponse.java b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/dto/response/MemberGetResponse.java similarity index 71% rename from second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/dto/response/MemberGetResponse.java rename to second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/dto/response/MemberGetResponse.java index c518e45..0beeccc 100644 --- a/second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/dto/response/MemberGetResponse.java +++ b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/dto/response/MemberGetResponse.java @@ -1,7 +1,7 @@ -package thirdseminar.thirdseminar.dto.response; +package secondSeminar.secondSeminar.dto.response; -import thirdseminar.thirdseminar.domain.Member; -import thirdseminar.thirdseminar.domain.SOPT; +import secondSeminar.secondSeminar.domain.Member; +import secondSeminar.secondSeminar.domain.SOPT; public record MemberGetResponse( String name, diff --git a/second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/exception/BusinessException.java b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/exception/BusinessException.java similarity index 74% rename from second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/exception/BusinessException.java rename to second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/exception/BusinessException.java index ed1fbc7..b7aa3be 100644 --- a/second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/exception/BusinessException.java +++ b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/exception/BusinessException.java @@ -1,4 +1,4 @@ -package thirdseminar.thirdseminar.exception; +package secondSeminar.secondSeminar.exception; public class BusinessException extends RuntimeException { public BusinessException(String message) { diff --git a/second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/exception/ErrorMessage.java b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/exception/ErrorMessage.java similarity index 92% rename from second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/exception/ErrorMessage.java rename to second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/exception/ErrorMessage.java index 3753116..7af3a3c 100644 --- a/second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/exception/ErrorMessage.java +++ b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/exception/ErrorMessage.java @@ -1,4 +1,4 @@ -package thirdseminar.thirdseminar.exception; +package secondSeminar.secondSeminar.exception; import lombok.AccessLevel; import lombok.AllArgsConstructor; diff --git a/second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/exception/GlobalExceptionHandler.java b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/exception/GlobalExceptionHandler.java similarity index 93% rename from second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/exception/GlobalExceptionHandler.java rename to second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/exception/GlobalExceptionHandler.java index a8b897b..5310a6f 100644 --- a/second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/exception/GlobalExceptionHandler.java +++ b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/exception/GlobalExceptionHandler.java @@ -1,4 +1,4 @@ -package thirdseminar.thirdseminar.exception; +package secondSeminar.secondSeminar.exception; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ExceptionHandler; diff --git a/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/external/S3Service.java b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/external/S3Service.java new file mode 100644 index 0000000..24568f9 --- /dev/null +++ b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/external/S3Service.java @@ -0,0 +1,80 @@ +package secondSeminar.secondSeminar.external; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.springframework.web.multipart.MultipartFile; +import secondSeminar.secondSeminar.config.AwsConfig; +import software.amazon.awssdk.core.sync.RequestBody; +import software.amazon.awssdk.services.s3.S3Client; +import software.amazon.awssdk.services.s3.model.DeleteObjectRequest; +import software.amazon.awssdk.services.s3.model.GetUrlRequest; +import software.amazon.awssdk.services.s3.model.PutObjectRequest; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.UUID; + +@Component +public class S3Service { + + private final String bucketName; + private final AwsConfig awsConfig; + + private static final List IMAGE_EXTENSIONS = Arrays.asList("image/jpeg", "image/png", "image/jpg", "image/webp"); + private static final Long MAX_FILE_SIZE = 5 * 1024 * 1024L; + + public S3Service(@Value("${aws-property.s3-bucket-name}") final String bucketName, AwsConfig awsConfig) { + this.bucketName = bucketName; + this.awsConfig = awsConfig; + } + + + public String uploadImage(String directoryPath, MultipartFile image) throws IOException { + final String key = directoryPath + generateImageFileName(); + final S3Client s3Client = awsConfig.getS3Client(); + + validateExtension(image); + validateFileSize(image); + + PutObjectRequest request = PutObjectRequest.builder() + .bucket(bucketName) + .key(key) + .contentType(image.getContentType()) + .contentDisposition("inline") + .build(); + + RequestBody requestBody = RequestBody.fromBytes(image.getBytes()); + s3Client.putObject(request, requestBody); + return key; + } + + private void validateExtension(MultipartFile image) { + String contentType = image.getContentType(); + if (!IMAGE_EXTENSIONS.contains(contentType)) { + throw new RuntimeException("이미지 확장자는 jpg, png, webp만 가능합니다."); + } + } + + private void validateFileSize(MultipartFile image) { + if (image.getSize() > MAX_FILE_SIZE) { + throw new RuntimeException("이미지 사이즈는 5MB를 넘을 수 없습니다."); + } + } + + public void deleteImage(String key) throws IOException { + final S3Client s3Client = awsConfig.getS3Client(); + + s3Client.deleteObject((DeleteObjectRequest.Builder builder) -> + builder.bucket(bucketName) + .key(key) + .build() + ); + } + + + private String generateImageFileName() { + return UUID.randomUUID().toString() + ".jpg"; + } + +} diff --git a/second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/repository/CategoryJpaRepository.java b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/repository/CategoryJpaRepository.java similarity index 59% rename from second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/repository/CategoryJpaRepository.java rename to second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/repository/CategoryJpaRepository.java index 1db089a..d627ea7 100644 --- a/second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/repository/CategoryJpaRepository.java +++ b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/repository/CategoryJpaRepository.java @@ -1,7 +1,7 @@ -package thirdseminar.thirdseminar.repository; +package secondSeminar.secondSeminar.repository; import org.springframework.data.jpa.repository.JpaRepository; -import thirdseminar.thirdseminar.domain.Category; +import secondSeminar.secondSeminar.domain.Category; public interface CategoryJpaRepository extends JpaRepository { diff --git a/second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/repository/MemberJpaRepository.java b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/repository/MemberJpaRepository.java similarity index 66% rename from second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/repository/MemberJpaRepository.java rename to second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/repository/MemberJpaRepository.java index 1313c28..b6be275 100644 --- a/second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/repository/MemberJpaRepository.java +++ b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/repository/MemberJpaRepository.java @@ -1,10 +1,10 @@ -package thirdseminar.thirdseminar.repository; +package secondSeminar.secondSeminar.repository; -import static thirdseminar.thirdseminar.exception.ErrorMessage.MEMBER_NOT_FOUND_EXCEPTION; +import static secondSeminar.secondSeminar.exception.ErrorMessage.MEMBER_NOT_FOUND_EXCEPTION; import jakarta.persistence.EntityNotFoundException; import org.springframework.data.jpa.repository.JpaRepository; -import thirdseminar.thirdseminar.domain.Member; +import secondSeminar.secondSeminar.domain.Member; public interface MemberJpaRepository extends JpaRepository { default Member findByIdOrThrow(Long id) { diff --git a/second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/repository/MemberRepository.java b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/repository/MemberRepository.java similarity index 58% rename from second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/repository/MemberRepository.java rename to second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/repository/MemberRepository.java index 6f7f78d..76e689d 100644 --- a/second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/repository/MemberRepository.java +++ b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/repository/MemberRepository.java @@ -1,7 +1,7 @@ -package thirdseminar.thirdseminar.repository; +package secondSeminar.secondSeminar.repository; import org.springframework.data.jpa.repository.JpaRepository; -import thirdseminar.thirdseminar.domain.Member; +import secondSeminar.secondSeminar.domain.Member; public interface MemberRepository extends JpaRepository { diff --git a/second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/repository/PostJpaRepository.java b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/repository/PostJpaRepository.java similarity index 63% rename from second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/repository/PostJpaRepository.java rename to second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/repository/PostJpaRepository.java index 013820c..d7def3b 100644 --- a/second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/repository/PostJpaRepository.java +++ b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/repository/PostJpaRepository.java @@ -1,9 +1,9 @@ -package thirdseminar.thirdseminar.repository; +package secondSeminar.secondSeminar.repository; import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; -import thirdseminar.thirdseminar.domain.Member; -import thirdseminar.thirdseminar.domain.Post; +import secondSeminar.secondSeminar.domain.Member; +import secondSeminar.secondSeminar.domain.Post; public interface PostJpaRepository extends JpaRepository { List findAllByMemberId(Long memberId); diff --git a/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/repository/ServiceMemberJpaRepository.java b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/repository/ServiceMemberJpaRepository.java new file mode 100644 index 0000000..d9c1cbe --- /dev/null +++ b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/repository/ServiceMemberJpaRepository.java @@ -0,0 +1,9 @@ +package secondSeminar.secondSeminar.repository; + +import java.util.Optional; +import org.springframework.data.jpa.repository.JpaRepository; +import secondSeminar.secondSeminar.domain.ServiceMember; + +public interface ServiceMemberJpaRepository extends JpaRepository { + Optional findByNickname(String nickname); +} diff --git a/second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/service/CategoryService.java b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/service/CategoryService.java similarity index 58% rename from second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/service/CategoryService.java rename to second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/service/CategoryService.java index c9d7bc4..0212db0 100644 --- a/second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/service/CategoryService.java +++ b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/service/CategoryService.java @@ -1,13 +1,14 @@ -package thirdseminar.thirdseminar.service; +package secondSeminar.secondSeminar.service; import jakarta.persistence.EntityNotFoundException; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import thirdseminar.thirdseminar.domain.Category; -import thirdseminar.thirdseminar.dto.response.CategoryResponse; -import thirdseminar.thirdseminar.exception.ErrorMessage; -import thirdseminar.thirdseminar.repository.CategoryJpaRepository; +import secondSeminar.secondSeminar.domain.Category; +import secondSeminar.secondSeminar.domain.CategoryId; +import secondSeminar.secondSeminar.dto.response.CategoryResponse; +import secondSeminar.secondSeminar.exception.ErrorMessage; +import secondSeminar.secondSeminar.repository.CategoryJpaRepository; @Service @RequiredArgsConstructor @@ -16,8 +17,8 @@ public class CategoryService { private final CategoryJpaRepository categoryJpaRepository; - public Category getByCategoryId(Short categoryId) { - return categoryJpaRepository.findById(categoryId).orElseThrow( + public Category getByCategoryId(CategoryId categoryId) { + return categoryJpaRepository.findById(Short.valueOf(categoryId.getCategoryId())).orElseThrow( () -> new EntityNotFoundException(ErrorMessage.CATEGORY_NOT_FOUND_EXCEPTION.getMessage())); } diff --git a/second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/service/MemberService.java b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/service/MemberService.java similarity index 82% rename from second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/service/MemberService.java rename to second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/service/MemberService.java index 591f8f6..e4b3075 100644 --- a/second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/service/MemberService.java +++ b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/service/MemberService.java @@ -1,4 +1,4 @@ -package thirdseminar.thirdseminar.service; +package secondSeminar.secondSeminar.service; import jakarta.persistence.EntityNotFoundException; import java.util.List; @@ -6,13 +6,13 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import thirdseminar.thirdseminar.domain.Member; -import thirdseminar.thirdseminar.domain.SOPT; -import thirdseminar.thirdseminar.dto.request.member.MemberCreateRequest; -import thirdseminar.thirdseminar.dto.request.member.MemberProfileUpdateRequest; -import thirdseminar.thirdseminar.dto.response.MemberGetResponse; -import thirdseminar.thirdseminar.exception.ErrorMessage; -import thirdseminar.thirdseminar.repository.MemberJpaRepository; +import secondSeminar.secondSeminar.domain.Member; +import secondSeminar.secondSeminar.domain.SOPT; +import secondSeminar.secondSeminar.dto.request.member.MemberCreateRequest; +import secondSeminar.secondSeminar.dto.request.member.MemberProfileUpdateRequest; +import secondSeminar.secondSeminar.dto.response.MemberGetResponse; +import secondSeminar.secondSeminar.exception.ErrorMessage; +import secondSeminar.secondSeminar.repository.MemberJpaRepository; @Service diff --git a/second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/service/PostService.java b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/service/PostService.java similarity index 77% rename from second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/service/PostService.java rename to second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/service/PostService.java index 2e130b7..e6c6ee6 100644 --- a/second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/service/PostService.java +++ b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/service/PostService.java @@ -1,19 +1,19 @@ -package thirdseminar.thirdseminar.service; +package secondSeminar.secondSeminar.service; import jakarta.persistence.EntityNotFoundException; import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import thirdseminar.thirdseminar.domain.Category; -import thirdseminar.thirdseminar.domain.Member; -import thirdseminar.thirdseminar.domain.Post; -import thirdseminar.thirdseminar.dto.request.post.PostCreateRequest; -import thirdseminar.thirdseminar.dto.request.post.PostGetResponse; -import thirdseminar.thirdseminar.dto.request.post.PostUpdateRequest; -import thirdseminar.thirdseminar.exception.ErrorMessage; -import thirdseminar.thirdseminar.repository.MemberJpaRepository; -import thirdseminar.thirdseminar.repository.PostJpaRepository; +import secondSeminar.secondSeminar.domain.Category; +import secondSeminar.secondSeminar.domain.Member; +import secondSeminar.secondSeminar.domain.Post; +import secondSeminar.secondSeminar.dto.request.post.PostCreateRequest; +import secondSeminar.secondSeminar.dto.request.post.PostGetResponse; +import secondSeminar.secondSeminar.dto.request.post.PostUpdateRequest; +import secondSeminar.secondSeminar.exception.ErrorMessage; +import secondSeminar.secondSeminar.repository.MemberJpaRepository; +import secondSeminar.secondSeminar.repository.PostJpaRepository; @Service @RequiredArgsConstructor @@ -33,7 +33,7 @@ public String create(PostCreateRequest request, Long memberId) { .member(member) .title(request.title()) .content(request.content()).build()); - return post.getPostId().toString(); + return post.getId().toString(); } @Transactional diff --git a/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/service/PostServiceV2.java b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/service/PostServiceV2.java new file mode 100644 index 0000000..dea651f --- /dev/null +++ b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/service/PostServiceV2.java @@ -0,0 +1,56 @@ +package secondSeminar.secondSeminar.service; + +import java.io.IOException; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; +import secondSeminar.secondSeminar.domain.Member; +import secondSeminar.secondSeminar.domain.Post; +import secondSeminar.secondSeminar.dto.request.post.PostCreateRequest; +import secondSeminar.secondSeminar.exception.BusinessException; +import secondSeminar.secondSeminar.external.S3Service; +import secondSeminar.secondSeminar.repository.MemberJpaRepository; +import secondSeminar.secondSeminar.repository.PostJpaRepository; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class PostServiceV2 { + + private static final String POST_IMAGE_FOLDER_NAME = "posts/"; + + private final MemberJpaRepository memberJpaRepository; + private final PostJpaRepository postJpaRepository; + private final S3Service s3Service; + + @Transactional + public String createV2(final PostCreateRequest request, final MultipartFile image, final Long memberId) { + try { + final String imageUrl = s3Service.uploadImage(POST_IMAGE_FOLDER_NAME, image); + Member member = memberJpaRepository.findByIdOrThrow(memberId); + Post post = postJpaRepository.save( + Post.builderWithImageUrl() + .title(request.title()) + .content(request.content()) + .imageUrl(imageUrl) + .member(member) + .build()); + return post.getId().toString(); + } catch (RuntimeException | IOException e) { + throw new RuntimeException(e.getMessage()); + } + } + + @Transactional + public void deleteByIdV2(Long postId) { + try { + Post post = postJpaRepository.findById(postId) + .orElseThrow(() -> new BusinessException("해당하는 게시글이 없습니다.")); + s3Service.deleteImage(post.getImageUrl()); + postJpaRepository.deleteById(postId); + } catch (IOException | RuntimeException e) { + throw new RuntimeException(e.getMessage()); + } + } +} diff --git a/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/service/ServiceMemberService.java b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/service/ServiceMemberService.java new file mode 100644 index 0000000..5f8fa08 --- /dev/null +++ b/second-seminar/second-seminar/src/main/java/secondSeminar/secondSeminar/service/ServiceMemberService.java @@ -0,0 +1,38 @@ +package secondSeminar.secondSeminar.service; + +import lombok.RequiredArgsConstructor; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import secondSeminar.secondSeminar.domain.ServiceMember; +import secondSeminar.secondSeminar.dto.request.servicemember.ServiceMemberRequest; +import secondSeminar.secondSeminar.repository.ServiceMemberJpaRepository; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class ServiceMemberService { + + private final ServiceMemberJpaRepository serviceMemberJpaRepository; + private final PasswordEncoder passwordEncoder; + + @Transactional + public String create(ServiceMemberRequest request) { + ServiceMember serviceMember = ServiceMember.builder() + .nickname(request.nickname()) + .password(passwordEncoder.encode(request.password())) + .build(); + serviceMemberJpaRepository.save(serviceMember); + + return serviceMember.getId().toString(); + } + + public void signIn(ServiceMemberRequest request) { + ServiceMember serviceMember = serviceMemberJpaRepository.findByNickname(request.nickname()) + .orElseThrow(() -> new RuntimeException("해당하는 회원이 없습니다.")); + if (!passwordEncoder.matches(request.password(), serviceMember.getPassword())) { + throw new RuntimeException("비밀번호가 일치하지 않습니다."); + } + } + +} diff --git a/second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/config/SecurityConfig.java b/second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/config/SecurityConfig.java deleted file mode 100644 index d186693..0000000 --- a/second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/config/SecurityConfig.java +++ /dev/null @@ -1,34 +0,0 @@ -package thirdseminar.thirdseminar.config; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.web.SecurityFilterChain; -import org.springframework.web.servlet.config.annotation.CorsRegistry; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; - -@Configuration -@EnableWebSecurity -public class SecurityConfig { - @Bean - SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { - return http.csrf().disable() - .authorizeHttpRequests() - .anyRequest().permitAll() - .and().build(); - } - - @Bean - public WebMvcConfigurer corsConfigurer() { - return new WebMvcConfigurer() { - @Override - public void addCorsMappings(CorsRegistry registry) { - registry.addMapping("/**") - .allowedOrigins("*") - .allowedOriginPatterns("*") - .allowedMethods("*"); - } - }; - } -} diff --git a/second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/dto/request/member/MemberProfileUpdateRequest.java b/second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/dto/request/member/MemberProfileUpdateRequest.java deleted file mode 100644 index 9091d64..0000000 --- a/second-seminar/second-seminar/src/main/java/thirdseminar/thirdseminar/dto/request/member/MemberProfileUpdateRequest.java +++ /dev/null @@ -1,9 +0,0 @@ -package thirdseminar.thirdseminar.dto.request.member; - -import thirdseminar.thirdseminar.domain.enums.Part; - - -public record MemberProfileUpdateRequest ( - int generation, - Part part -){} diff --git a/second-seminar/second-seminar/src/test/java/thirdseminar/thirdseminar/SecondSeminarApplicationTests.java b/second-seminar/second-seminar/src/test/java/secondSeminar/secondSeminar/SecondSeminarApplicationTests.java similarity index 83% rename from second-seminar/second-seminar/src/test/java/thirdseminar/thirdseminar/SecondSeminarApplicationTests.java rename to second-seminar/second-seminar/src/test/java/secondSeminar/secondSeminar/SecondSeminarApplicationTests.java index 226d276..6da19e1 100644 --- a/second-seminar/second-seminar/src/test/java/thirdseminar/thirdseminar/SecondSeminarApplicationTests.java +++ b/second-seminar/second-seminar/src/test/java/secondSeminar/secondSeminar/SecondSeminarApplicationTests.java @@ -1,4 +1,4 @@ -package thirdseminar.thirdseminar; +package secondSeminar.secondSeminar; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest;