-
Notifications
You must be signed in to change notification settings - Fork 0
[FEAT] 회원가입 #14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[FEAT] 회원가입 #14
Conversation
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the WalkthroughThis update introduces foundational backend features for a Spring Boot application, including member management, security configuration, JWT token handling, and a comprehensive set of JPA entities for users, posts, tags, and relationships. It also standardizes package naming, configures H2 database settings, and implements robust exception handling and API response structures. Changes
Sequence Diagram(s)Member Signup and Duplicate Check FlowsequenceDiagram
participant Client
participant MemberController
participant MemberService
participant MemberRepository
Client->>MemberController: POST /api/members/signup (SignupRequestDto)
MemberController->>MemberService: signup(SignupRequestDto)
MemberService->>MemberRepository: findByEmail(email)
MemberService->>MemberRepository: findByNickname(nickname)
MemberService-->>MemberRepository: save(Member)
MemberService-->>MemberController: void
MemberController-->>Client: Response<String> (success)
Client->>MemberController: GET /api/members/check-email?email=...
MemberController->>MemberService: isEmailDuplicated(email)
MemberService->>MemberRepository: findByEmail(email)
MemberService-->>MemberController: boolean
MemberController-->>Client: Response<DuplicateCheckResponseDto>
Client->>MemberController: GET /api/members/check-nickname?nickname=...
MemberController->>MemberService: isNicknameDuplicated(nickname)
MemberService->>MemberRepository: findByNickname(nickname)
MemberService-->>MemberController: boolean
MemberController-->>Client: Response<DuplicateCheckResponseDto>
JWT Token Creation and ValidationsequenceDiagram
participant Service
participant JwtTokenProvider
Service->>JwtTokenProvider: createToken(email)
JwtTokenProvider-->>Service: JWT token string
Service->>JwtTokenProvider: validateToken(token)
JwtTokenProvider-->>Service: boolean
Service->>JwtTokenProvider: getEmailFromToken(token)
JwtTokenProvider-->>Service: email (String)
Poem
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 22
🧹 Nitpick comments (28)
src/main/java/com/example/fixlog/FixLogApplication.java (1)
5-5: Good addition of JPA auditing support.Enabling JPA auditing with
@EnableJpaAuditingis appropriate for tracking entity creation and modification timestamps.Consider using English instead of Korean for comments (line 8) to improve code readability for international collaboration:
-//Created At 설정 +//Enable automatic timestamp management for entitiesAlso applies to: 8-9
src/main/java/com/example/fixlog/domain/tag/TagCategory.java (1)
1-7: Good use of enum for tag categorization.Using an enum for tag categories provides type safety and helps prevent invalid category values.
Consider adding JavaDoc comments to explain the purpose of each category:
package com.example.fixlog.domain.tag; +/** + * Represents categories for classifying tags in the application. + */ public enum TagCategory { + /** Tags related to visual or design style */ STYLE, + /** Tags related to conceptual themes */ CONCEPT, + /** Other miscellaneous tags */ ETC }src/main/java/com/example/fixlog/dto/memberdto/SignupRequestDto.java (1)
10-10: Translate comments to English or remove
The inline Korean comment may reduce readability for non-Korean speakers. Consider translating, for example:// Password confirmation is handled on the frontendor removing it if unnecessary.
src/main/resources/application.properties (1)
1-15: Separate environment-specific configurations
For better maintainability and to avoid leaking dev-only settings into prod:
- Create
application-dev.propertiesfor H2 console and local DB settings.- Use an in-memory URL (
jdbc:h2:mem:fixlog;DB_CLOSE_DELAY=-1) for tests.- Secure the H2 console behind authentication or a dedicated profile.
src/main/java/com/example/fixlog/repository/MemberRepository.java (1)
8-11: Optimize duplicate checks and enforce uniqueness
- Define
boolean existsByEmail(String email)andboolean existsByNickname(String nickname)for direct, efficient checks.- Ensure the
Memberentity has@Column(unique = true)(or equivalent) on bothnicknameto enforce uniqueness at the database level and guard against race conditions.src/main/java/com/example/fixlog/exception/CustomException.java (1)
6-10: Good implementation of a custom exception class.The
CustomExceptionclass is well-structured with proper use of Lombok annotations to reduce boilerplate code. This design allows for specific error codes to be associated with exceptions, enabling detailed error information to be communicated to clients.Consider adding a constructor that accepts both an
ErrorCodeand a custom message for cases where you might want to provide additional context beyond what's in theErrorCodeenum.src/main/java/com/example/fixlog/dto/memberdto/DuplicateCheckResponseDto.java (1)
6-10: Clean DTO implementation for duplicate checking.This simple DTO class follows the single responsibility principle and effectively encapsulates the duplication check result.
For better readability when used in client code, consider renaming the field to
isDuplicatedto follow Java bean naming conventions for boolean properties.@Getter @AllArgsConstructor public class DuplicateCheckResponseDto { - private boolean duplicated; + private boolean isDuplicated; }src/main/java/com/example/fixlog/domain/post/PostImage.java (1)
1-24: Entity design looks good, but missing constructor methodThe PostImage entity is well-structured with proper JPA annotations and relationships. The lazy loading for the Post relationship is a good practice to avoid unnecessary database queries.
However, you should consider adding a factory method or constructor to create a PostImage instance and establish the relationship with a Post, similar to the Member.of() pattern used in Member.java.
public static PostImage of(String postImageUrl, Post post) { PostImage postImage = new PostImage(); postImage.postImageUrl = postImageUrl; postImage.post = post; return postImage; }src/main/java/com/example/fixlog/domain/fork/Fork.java (1)
17-18: Add nullable = false to be consistentFor consistency with other entities, add the nullable = false attribute to the forkId column.
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) -@Column(name = "forkId") +@Column(name = "forkId", nullable = false) private Long id;src/main/java/com/example/fixlog/exception/ErrorCode.java (1)
1-15: Good implementation of error codes with appropriate HTTP statuses.The ErrorCode enum is well structured with HttpStatus and message fields, properly supporting the signup functionality mentioned in the PR objectives. The HTTP status of CONFLICT (409) is correctly chosen for duplicate resource scenarios.
A few suggestions for improvement:
- Consider adding Javadoc to document the purpose of this enum
- If the application will be used internationally, consider implementing i18n instead of hardcoded Korean messages
src/main/java/com/example/fixlog/domain/follow/Follow.java (1)
9-26: Well-structured Follow entity with appropriate JPA relationships.The implementation uses best practices for JPA:
- Lazy loading for associated entities
- Proper column naming and nullability constraints
- Protected no-args constructor as required by JPA
Consider these improvements:
- Add a unique constraint to prevent duplicate follow relationships
@Entity @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) +@Table(uniqueConstraints = { + @UniqueConstraint(columnNames = {"followerId", "followingId"}) +}) public class Follow {
- Add a factory method to create Follow instances
public static Follow of(Member follower, Member following) { Follow follow = new Follow(); follow.follower = follower; follow.following = following; return follow; }
- Consider adding audit fields for tracking when follow relationships are created
src/main/java/com/example/fixlog/service/MemberService.java (2)
42-44: Consider adding transaction attribute for read-only operations.Since this method only reads data, it should be marked as read-only:
+@Transactional(readOnly = true) public boolean isEmailDuplicated(String email) { return memberRepository.findByEmail(email).isPresent(); }
46-48: Consider adding transaction attribute for read-only operations.Since this method only reads data, it should be marked as read-only:
+@Transactional(readOnly = true) public boolean isNicknameDuplicated(String nickname) { return memberRepository.findByNickname(nickname).isPresent(); }src/main/java/com/example/fixlog/domain/member/Member.java (1)
16-26: Well-structured entity with good JPA annotationsThe entity is properly defined with JPA annotations and follows good practices with the use of @EntityListeners for auditing.
Consider adding indexes for fields that will be frequently used in queries, especially
nicknamewhich appear to be used for lookup operations:@Entity @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) @EntityListeners(AuditingEntityListener.class) +@Table(indexes = { + @Index(name = "idx_member_email", columnList = "email", unique = true), + @Index(name = "idx_member_nickname", columnList = "nickname", unique = true) +}) public class Member {src/main/java/com/example/fixlog/domain/bookmark/BookmarkTag.java (2)
3-4: Remove unused importThe
Tagclass is imported but not used in this file.-import com.example.fixlog.domain.tag.Tag; import com.example.fixlog.domain.tag.TagCategory;
14-26: Add relationship mapping and factory methodThis entity is missing:
- A bidirectional relationship with
BookmarkTagMap- A factory method for creating instances
Consider adding:
@Entity @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) public class BookmarkTag { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "bookmarkTagId",nullable = false) private Long id; @Column(length = 20, nullable = false) private String tagName; @Enumerated(EnumType.STRING) @Column(nullable = false) private TagCategory tagCategory; + @OneToMany(mappedBy = "bookmarkTag", cascade = CascadeType.ALL, orphanRemoval = true) + private List<BookmarkTagMap> bookmarkTagMaps = new ArrayList<>(); + + public static BookmarkTag of(String tagName, TagCategory tagCategory) { + if (tagName == null || tagName.trim().isEmpty()) { + throw new IllegalArgumentException("Tag name cannot be null or empty"); + } + if (tagCategory == null) { + throw new IllegalArgumentException("Tag category cannot be null"); + } + + BookmarkTag bookmarkTag = new BookmarkTag(); + bookmarkTag.tagName = tagName; + bookmarkTag.tagCategory = tagCategory; + return bookmarkTag; + } }src/main/java/com/example/fixlog/domain/bookmark/Bookmark.java (1)
15-29: Add auditing and factory methodConsider enhancing the entity with:
- Creation/modification timestamps using JPA auditing
- A factory method for creating instances
- A bidirectional relationship with BookmarkTagMap
@Entity @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) +@EntityListeners(AuditingEntityListener.class) @Table(uniqueConstraints = { @UniqueConstraint(name = "uk_bookmark_member_post", columnNames = {"userId", "postId"}) }) public class Bookmark { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name="bookmarkId") private Long id; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "userId") private Member member; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "postId") private Post post; + @CreatedDate + @Column(updatable = false) + private LocalDateTime createdAt; + + @OneToMany(mappedBy = "bookmark", cascade = CascadeType.ALL, orphanRemoval = true) + private List<BookmarkTagMap> bookmarkTagMaps = new ArrayList<>(); + + public static Bookmark of(Member member, Post post) { + if (member == null) { + throw new IllegalArgumentException("Member cannot be null"); + } + if (post == null) { + throw new IllegalArgumentException("Post cannot be null"); + } + + Bookmark bookmark = new Bookmark(); + bookmark.member = member; + bookmark.post = post; + return bookmark; + } }Don't forget to add the corresponding import statements for LocalDateTime, CreatedDate, List, ArrayList, etc.
src/main/java/com/example/fixlog/domain/tag/Tag.java (1)
11-22: Consider adding a constructor for entity creationThe entity lacks a constructor to create new Tag instances, which would make it more convenient to use. Also, consider adding consistency with BookmarkTag which has similar fields directly embedded.
@Entity @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) public class Tag { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "tagId",nullable = false) private Long id; @Enumerated(EnumType.STRING) private TagCategory tagCategory; @Column(length = 20, nullable = false) private String tagName; + + public Tag(TagCategory tagCategory, String tagName) { + this.tagCategory = tagCategory; + this.tagName = tagName; + } }src/main/java/com/example/fixlog/domain/bookmark/BookmarkTagMap.java (2)
8-8: Remove extra blank lineThere's an unnecessary blank line that can be removed.
import lombok.Getter; import lombok.NoArgsConstructor; - @Entity
12-26: Consider adding constructor and association methodsThe entity lacks a constructor for creating new mappings, which would make it more convenient to use. Also, providing methods to manage the associations would improve usability.
@Entity @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) public class BookmarkTagMap { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "mapId", nullable = false) private Long id; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "bookmarkId", nullable = false) private Bookmark bookmark; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "bookmarkTagId", nullable = false) private BookmarkTag bookmarkTag; + + public BookmarkTagMap(Bookmark bookmark, BookmarkTag bookmarkTag) { + this.bookmark = bookmark; + this.bookmarkTag = bookmarkTag; + } }src/main/java/com/example/fixlog/controller/MemberController.java (3)
24-36: Extract duplicate code pattern in email and nickname checksThe email and nickname check endpoints have nearly identical code structure. Consider extracting this pattern to a private method to reduce duplication.
@GetMapping("/check-email") public ResponseEntity<Response<DuplicateCheckResponseDto>> checkEmail(@RequestParam String email) { - boolean exists = memberService.isEmailDuplicated(email); - String msg = exists ? "이미 사용 중인 이메일입니다." : "사용 가능한 이메일입니다."; - return ResponseEntity.ok(Response.success(msg, new DuplicateCheckResponseDto(exists))); + return checkDuplication( + memberService.isEmailDuplicated(email), + "이미 사용 중인 이메일입니다.", + "사용 가능한 이메일입니다." + ); } @GetMapping("/check-nickname") public ResponseEntity<Response<DuplicateCheckResponseDto>> checkNickname(@RequestParam String nickname) { - boolean exists = memberService.isNicknameDuplicated(nickname); - String msg = exists ? "이미 사용 중인 닉네임입니다." : "사용 가능한 닉네임입니다."; - return ResponseEntity.ok(Response.success(msg, new DuplicateCheckResponseDto(exists))); + return checkDuplication( + memberService.isNicknameDuplicated(nickname), + "이미 사용 중인 닉네임입니다.", + "사용 가능한 닉네임입니다." + ); } +private ResponseEntity<Response<DuplicateCheckResponseDto>> checkDuplication( + boolean exists, String existsMessage, String notExistsMessage) { + String msg = exists ? existsMessage : notExistsMessage; + return ResponseEntity.ok(Response.success(msg, new DuplicateCheckResponseDto(exists))); +}
1-37: Consider adding API documentationThe controller lacks API documentation, which would be helpful for API consumers. Consider adding Swagger annotations to document the API endpoints.
Add at the class level:
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; @RestController @RequestMapping("/api/members") @RequiredArgsConstructor @Tag(name = "Member API", description = "Member management endpoints") public class MemberController {And for each method:
@Operation(summary = "Sign up a new member", description = "Register a new member with email, nickname, and password") @PostMapping("/signup") public ResponseEntity<Response<String>> signup(@RequestBody @Valid SignupRequestDto request) { // ... } @Operation(summary = "Check email duplication", description = "Check if an email is already in use") @GetMapping("/check-email") public ResponseEntity<Response<DuplicateCheckResponseDto>> checkEmail(@RequestParam String email) { // ... }
21-21: Consider returning meaningful data in the responseThe success response for signup returns null as data. Consider returning something more meaningful, such as the created member's ID or email.
- return ResponseEntity.ok(Response.success("회원가입이 완료되었습니다.", null)); + return ResponseEntity.ok(Response.success("회원가입이 완료되었습니다.", request.getEmail()));src/main/java/com/example/fixlog/jwt/JwtTokenProvider.java (2)
15-22: Add more claims to the tokenConsider adding more claims to the JWT token to provide additional context for the token, such as user roles, token type, or issuer information.
public String createToken(String email) { return Jwts.builder() .setSubject(email) .setIssuedAt(new Date()) - .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME)) - .signWith(Keys.hmacShaKeyFor(SECRET_KEY.getBytes()), SignatureAlgorithm.HS256) + .setExpiration(new Date(System.currentTimeMillis() + expirationTime)) + .claim("type", "access") + .setIssuer("fixlog") + .signWith(Keys.hmacShaKeyFor(secretKey.getBytes()), SignatureAlgorithm.HS256) .compact(); }
9-41: Consider implementing refresh token mechanismThe current implementation only provides access tokens. For better security and user experience, consider implementing a refresh token mechanism.
You could add methods like:
public String createRefreshToken(String email) { return Jwts.builder() .setSubject(email) .setIssuedAt(new Date()) .setExpiration(new Date(System.currentTimeMillis() + (expirationTime * 7))) // 7 days .claim("type", "refresh") .setIssuer("fixlog") .signWith(Keys.hmacShaKeyFor(secretKey.getBytes()), SignatureAlgorithm.HS256) .compact(); } public TokenPair createTokenPair(String email) { return new TokenPair( createToken(email), createRefreshToken(email) ); }And create a TokenPair class:
public class TokenPair { private final String accessToken; private final String refreshToken; public TokenPair(String accessToken, String refreshToken) { this.accessToken = accessToken; this.refreshToken = refreshToken; } // Getters }src/main/java/com/example/fixlog/domain/post/Post.java (3)
30-31: Consider renaming the join column for clarity.The join column name "userId" is inconsistent with the field name "member". For better consistency and clarity, consider naming it "memberId" instead.
@ManyToOne(fetch = FetchType.LAZY) -@JoinColumn(name = "userId", nullable = false) +@JoinColumn(name = "memberId", nullable = false) private Member member;
13-16: Consider implementing equals and hashCode methods.JPA entities should generally override equals and hashCode methods for proper identity management in collections. Consider adding these methods using a business key or the ID field.
@Entity @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) +@EqualsAndHashCode(of = "id") public class Post {Alternatively, implement these methods manually if you need more control over the equality logic.
23-24: Consider adding Bean Validation annotations.While JPA column constraints provide database-level validation, adding Bean Validation annotations would provide validation at the application level as well.
+import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; @Column(length = 20, nullable = false) +@NotBlank +@Size(max = 20) private String title;
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting
📒 Files selected for processing (30)
build.gradle(1 hunks)src/main/java/com/example/fixlog/FixLogApplication.java(1 hunks)src/main/java/com/example/fixlog/config/SecurityConfig.java(1 hunks)src/main/java/com/example/fixlog/controller/MemberController.java(1 hunks)src/main/java/com/example/fixlog/domain/bookmark/Bookmark.java(1 hunks)src/main/java/com/example/fixlog/domain/bookmark/BookmarkTag.java(1 hunks)src/main/java/com/example/fixlog/domain/bookmark/BookmarkTagMap.java(1 hunks)src/main/java/com/example/fixlog/domain/follow/Follow.java(1 hunks)src/main/java/com/example/fixlog/domain/fork/Fork.java(1 hunks)src/main/java/com/example/fixlog/domain/like/PostLike.java(1 hunks)src/main/java/com/example/fixlog/domain/member/Member.java(1 hunks)src/main/java/com/example/fixlog/domain/member/SocialType.java(1 hunks)src/main/java/com/example/fixlog/domain/post/Post.java(1 hunks)src/main/java/com/example/fixlog/domain/post/PostImage.java(1 hunks)src/main/java/com/example/fixlog/domain/post/PostTag.java(1 hunks)src/main/java/com/example/fixlog/domain/tag/Tag.java(1 hunks)src/main/java/com/example/fixlog/domain/tag/TagCategory.java(1 hunks)src/main/java/com/example/fixlog/dto/Response.java(1 hunks)src/main/java/com/example/fixlog/dto/memberdto/DuplicateCheckResponseDto.java(1 hunks)src/main/java/com/example/fixlog/dto/memberdto/SignupRequestDto.java(1 hunks)src/main/java/com/example/fixlog/exception/CustomException.java(1 hunks)src/main/java/com/example/fixlog/exception/DuplicateEmailException.java(1 hunks)src/main/java/com/example/fixlog/exception/DuplicateNicknameException.java(1 hunks)src/main/java/com/example/fixlog/exception/ErrorCode.java(1 hunks)src/main/java/com/example/fixlog/exception/GlobalExceptionHandler.java(1 hunks)src/main/java/com/example/fixlog/jwt/JwtTokenProvider.java(1 hunks)src/main/java/com/example/fixlog/repository/MemberRepository.java(1 hunks)src/main/java/com/example/fixlog/service/MemberService.java(1 hunks)src/main/resources/application.properties(1 hunks)src/test/java/com/example/fixlog/FixLogApplicationTests.java(1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (6)
src/main/java/com/example/fixlog/dto/memberdto/DuplicateCheckResponseDto.java (2)
src/main/java/com/example/fixlog/dto/memberdto/SignupRequestDto.java (1)
Getter(6-13)src/main/java/com/example/fixlog/exception/CustomException.java (1)
Getter(6-10)
src/main/java/com/example/fixlog/domain/bookmark/BookmarkTag.java (3)
src/main/java/com/example/fixlog/domain/bookmark/Bookmark.java (1)
Entity(12-29)src/main/java/com/example/fixlog/domain/bookmark/BookmarkTagMap.java (1)
Entity(9-26)src/main/java/com/example/fixlog/domain/tag/Tag.java (1)
Entity(8-23)
src/main/java/com/example/fixlog/domain/fork/Fork.java (3)
src/main/java/com/example/fixlog/domain/member/Member.java (1)
Entity(16-65)src/main/java/com/example/fixlog/domain/bookmark/Bookmark.java (1)
Entity(12-29)src/main/java/com/example/fixlog/domain/post/Post.java (1)
Entity(13-38)
src/main/java/com/example/fixlog/domain/tag/Tag.java (2)
src/main/java/com/example/fixlog/domain/bookmark/BookmarkTag.java (1)
Entity(11-26)src/main/java/com/example/fixlog/domain/post/PostTag.java (1)
Entity(9-26)
src/main/java/com/example/fixlog/exception/ErrorCode.java (2)
src/main/java/com/example/fixlog/dto/memberdto/DuplicateCheckResponseDto.java (1)
Getter(6-10)src/main/java/com/example/fixlog/exception/CustomException.java (1)
Getter(6-10)
src/main/java/com/example/fixlog/domain/like/PostLike.java (3)
src/main/java/com/example/fixlog/domain/member/Member.java (1)
Entity(16-65)src/main/java/com/example/fixlog/domain/bookmark/Bookmark.java (1)
Entity(12-29)src/main/java/com/example/fixlog/domain/post/Post.java (1)
Entity(13-38)
🔇 Additional comments (9)
src/main/java/com/example/fixlog/FixLogApplication.java (1)
1-1: Package name standardization looks good.The change to lowercase package naming follows Java convention.
src/test/java/com/example/fixlog/FixLogApplicationTests.java (1)
1-1: Package name standardization is consistent.Good alignment with the main application package naming.
src/main/java/com/example/fixlog/domain/member/SocialType.java (1)
3-6: Enum implementation is correct
TheSocialTypeenum cleanly defines the supported social login methods (GITHUB), and the default mapping to JPA should work out of the box.src/main/java/com/example/fixlog/exception/DuplicateNicknameException.java (1)
3-6: Ensure exception integrates with global error handling
SinceGlobalExceptionHandleris set up to handleCustomExceptionwithErrorCode, consider either:
- Extending
CustomExceptionand passingErrorCode.NICKNAME_DUPLICATED, or- Annotating this class with
@ResponseStatus(HttpStatus.CONFLICT)This will guarantee duplicate-nickname errors yield HTTP 409 with the standardized error payload.
src/main/java/com/example/fixlog/domain/post/PostTag.java (3)
9-12: The entity class has appropriate JPA and Lombok annotations.Good use of
@Entity,@Getter, and protected constructor with@NoArgsConstructor(access = AccessLevel.PROTECTED)which follows JPA best practices.
14-17: Primary key is properly defined.The ID field uses appropriate annotations with
GenerationType.IDENTITYfor auto-generation and explicitly defines the column name.
19-25: Well-structured many-to-many relationship implementation.The relationships to
PostandTagare correctly set up as@ManyToOnewithLAZYfetching, which is a good choice for performance and preventing N+1 query issues.src/main/java/com/example/fixlog/dto/Response.java (1)
1-26: Well-structured response wrapper with good design patterns!The implementation follows solid design principles with:
- Good use of generics for flexibility
- Immutability through private constructor and factory methods
- Clean JSON responses via @JsonInclude
- Clear separation of success and failure responses
Consider adding JavaDoc comments to the public methods to better document their purpose and parameters, especially for other developers who will use this class.
src/main/java/com/example/fixlog/domain/post/Post.java (1)
16-37: Verify entity completeness for your domain requirements.The current Post entity appears to capture basic post information. Depending on your application requirements, consider if you need additional fields such as:
- Post status (draft, published, etc.)
- Privacy settings
- Categories or top-level tags
- View count
| implementation 'io.jsonwebtoken:jjwt-api:0.11.5' | ||
| runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5' | ||
| runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
JWT dependencies added correctly!
Good addition of JWT libraries with the correct separation of API (implementation) and implementation modules (runtime). This aligns with the PR objective of preparing for future JWT-based authentication.
🌐 Web query:
Is JWT version 0.11.5 the latest version available? Are there any known security vulnerabilities?
💡 Result:
No, JWT version 0.11.5 is not the latest version available. The current latest version is 0.12.6, which was released on June 21, 2024[10]. Version 0.11.5 was released on April 28, 2022[1][3].
Security Considerations
Version 0.11.5 was actually a security-focused release that added additional protections against a vulnerability in certain Java SE versions (15-15.0.6, 17-17.0.2, and 18) related to ECDSA signatures (CVE-2022-21449)[7]. This patch release added security guards to protect against this Java vulnerability, although the issue was not with JJWT itself but with the underlying JVM.
According to the search results, there was a reported CVE against version 0.11.5, but it was determined to be invalid. The issue was described as "reflection of an invalid test due to lack of understanding of JJWT's API" and its documentation on recommended practices[9].
However, the implementation module (jjwt-impl) version 0.11.5 does have some vulnerabilities reported from its dependencies:
Upgrade Recommendation
It is recommended to upgrade to the latest version (0.12.6) for several reasons:
- It likely includes all security fixes from previous versions
- It addresses potential dependency vulnerabilities
- It may contain performance improvements and new features
The JJWT library consists of multiple modules that work together:
- jjwt-api: The public API for creating and consuming JSON Web Tokens
- jjwt-impl: The implementation module
- jjwt-jackson: Extensions for Jackson integration
- Other modules for different JSON processors
When upgrading, you should update all related JJWT modules to maintain compatibility.
Citations:
- 1: https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-api/0.11.5
- 2: https://central.sonatype.com/artifact/io.jsonwebtoken/jjwt-jackson/0.11.5
- 3: https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-impl/0.11.5
- 4: https://javadoc.io/doc/io.jsonwebtoken/jjwt-api/0.11.5/io/jsonwebtoken/JwtBuilder.html
- 5: https://javadoc.io/doc/io.jsonwebtoken/jjwt-api/0.11.5/io/jsonwebtoken/Jwts.html
- 6: https://www.acunetix.com/blog/articles/json-web-token-jwt-attacks-vulnerabilities/
- 7: https://java.libhunt.com/jjwt-changelog
- 8: https://plugins.jenkins.io/jjwt-api/releases/
- 9: CVE reported against 0.11.5 jwtk/jjwt#930
- 10: https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt
Upgrade JJWT dependencies to latest version (0.12.6)
The current 0.11.5 artifacts are outdated and carry known dependency vulnerabilities. Please bump all JJWT modules to 0.12.6 for security fixes and compatibility.
• File: build.gradle (around lines 30–32)
• Update all three coordinates together to avoid mismatched versions
Suggested diff:
- implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
- runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5'
- runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5'
+ implementation 'io.jsonwebtoken:jjwt-api:0.12.6'
+ runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.6'
+ runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.6'📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| implementation 'io.jsonwebtoken:jjwt-api:0.11.5' | |
| runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5' | |
| runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5' | |
| implementation 'io.jsonwebtoken:jjwt-api:0.12.6' | |
| runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.6' | |
| runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.6' |
🤖 Prompt for AI Agents
In build.gradle around lines 30 to 32, the JJWT dependencies are using version
0.11.5, which is outdated and has known dependency vulnerabilities. Update all
three JJWT module versions (jjwt-api, jjwt-impl, jjwt-jackson) to 0.12.6
simultaneously to ensure compatibility and include the latest security fixes and
improvements.
| private String email; | ||
| //비밀번호 재확인은 프론트단에서 확인 | ||
| private String password; | ||
| private String nickname; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add server-side validation annotations
To enforce input constraints and improve API robustness, annotate fields, for example:
@NotBlank @Email
private String email;
@NotBlank @Size(min = 8)
private String password;
@NotBlank @Size(max = 20)
private String nickname;Then mark your controller parameter with @Valid.
🤖 Prompt for AI Agents
In src/main/java/com/example/fixlog/dto/memberdto/SignupRequestDto.java around
lines 9 to 12, the fields lack server-side validation annotations. Add
appropriate validation annotations such as @NotBlank and @Email for the email
field, @NotBlank and @Size(min = 8) for the password, and @NotBlank and
@Size(max = 20) for the nickname to enforce input constraints. Also, ensure the
controller method parameter using this DTO is annotated with @Valid to trigger
validation.
| public class DuplicateEmailException extends RuntimeException { | ||
| public DuplicateEmailException(String message) { | ||
| super(message); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Consider consolidating exception handling approach.
This specific exception type for email duplication creates inconsistency with the CustomException approach you've implemented. Having two different patterns for exception handling can lead to maintenance challenges.
For better consistency, consider using the CustomException with the appropriate ErrorCode instead of creating specific exception classes for each error scenario.
-package com.example.fixlog.exception;
-
-public class DuplicateEmailException extends RuntimeException {
- public DuplicateEmailException(String message) {
- super(message);
- }
-}Replace usages with:
throw new CustomException(ErrorCode.EMAIL_DUPLICATED);🤖 Prompt for AI Agents
In src/main/java/com/example/fixlog/exception/DuplicateEmailException.java lines
3 to 7, remove the DuplicateEmailException class entirely and replace all its
usages in the codebase with throwing a new CustomException using the
ErrorCode.EMAIL_DUPLICATED. This consolidates exception handling by using the
existing CustomException pattern and avoids maintaining multiple specific
exception classes.
| @Entity | ||
| @Getter | ||
| @NoArgsConstructor(access = AccessLevel.PROTECTED) | ||
| public class PostTag { | ||
|
|
||
| @Id | ||
| @GeneratedValue(strategy = GenerationType.IDENTITY) | ||
| @Column(name = "postTagId",nullable = false) | ||
| private Long id; | ||
|
|
||
| @ManyToOne(fetch = FetchType.LAZY) | ||
| @JoinColumn(name = "postId", nullable = false) | ||
| private Post post; | ||
|
|
||
| @ManyToOne(fetch = FetchType.LAZY) | ||
| @JoinColumn(name = "tagId", nullable = false) | ||
| private Tag tag; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Entity is missing bidirectional relationship management and constructor.
The entity doesn't provide a way to create new instances with the required associations. Also, consider implementing methods to manage bidirectional relationships (if they exist in the Post and Tag entities) and implementing equals and hashCode methods.
Add a constructor and utility methods:
public PostTag(Post post, Tag tag) {
this.post = post;
this.tag = tag;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof PostTag)) return false;
PostTag postTag = (PostTag) o;
return Objects.equals(id, postTag.id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}🤖 Prompt for AI Agents
In src/main/java/com/example/fixlog/domain/post/PostTag.java between lines 9 and
26, the PostTag entity lacks a constructor to create instances with associated
Post and Tag objects, and it does not manage bidirectional relationships or
override equals and hashCode methods. Add a public constructor accepting Post
and Tag parameters to initialize these fields. Implement equals and hashCode
methods based on the id field to ensure proper entity comparison. Additionally,
if Post and Tag entities have collections referencing PostTag, add utility
methods to maintain these bidirectional associations.
| http | ||
| .csrf(csrf -> csrf.disable()) | ||
| .authorizeHttpRequests(auth -> auth | ||
| .requestMatchers(HttpMethod.POST, "/api/members/signup").permitAll() | ||
| .requestMatchers(HttpMethod.GET, "/api/members/check-email").permitAll() | ||
| .requestMatchers(HttpMethod.GET, "/api/members/check-nickname").permitAll() | ||
| .anyRequest().authenticated() | ||
| ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Security configuration needs enhancement
While the basic security configuration works for the signup functionality, there are several security aspects that should be addressed:
- CSRF is disabled without explanation (though this may be appropriate for a stateless API)
- No CORS configuration is provided
- No session management strategy is defined (should be stateless for REST APIs)
- JWT integration mentioned in PR objective is missing
Consider enhancing the security configuration:
http
.csrf(csrf -> csrf.disable())
+ .cors(cors -> cors.configurationSource(corsConfigurationSource()))
+ .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(auth -> auth
.requestMatchers(HttpMethod.POST, "/api/members/signup").permitAll()
.requestMatchers(HttpMethod.GET, "/api/members/check-email").permitAll()
.requestMatchers(HttpMethod.GET, "/api/members/check-nickname").permitAll()
+ .requestMatchers("/h2-console/**").permitAll() // If you're using H2 console for development
.anyRequest().authenticated()
);
+
+ // Don't forget to add the import for SessionCreationPolicy and define the corsConfigurationSource method🤖 Prompt for AI Agents
In src/main/java/com/example/fixlog/config/SecurityConfig.java around lines 16
to 23, the security configuration disables CSRF without explanation, lacks CORS
configuration, does not define a session management strategy, and is missing JWT
integration. To fix this, explicitly configure CORS to allow appropriate
origins, set session management to stateless to suit REST API design, and
integrate JWT authentication by adding the necessary filters and authentication
providers. Also, document the reason for disabling CSRF to clarify the design
choice.
| public String getEmailFromToken(String token) { | ||
| return Jwts.parserBuilder() | ||
| .setSigningKey(SECRET_KEY.getBytes()) | ||
| .build() | ||
| .parseClaimsJws(token) | ||
| .getBody() | ||
| .getSubject(); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Improve error handling in getEmailFromToken method
The getEmailFromToken method doesn't handle potential exceptions when parsing the token. It will throw exceptions if the token is invalid, expired, or malformed.
public String getEmailFromToken(String token) {
+ try {
return Jwts.parserBuilder()
- .setSigningKey(SECRET_KEY.getBytes())
+ .setSigningKey(secretKey.getBytes())
.build()
.parseClaimsJws(token)
.getBody()
.getSubject();
+ } catch (JwtException e) {
+ throw new RuntimeException("Invalid JWT token: " + e.getMessage());
+ }
}Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/main/java/com/example/fixlog/jwt/JwtTokenProvider.java around lines 24 to
31, the getEmailFromToken method lacks error handling for exceptions thrown
during token parsing. Wrap the parsing logic in a try-catch block to catch
exceptions like expired, malformed, or invalid tokens, and handle them
appropriately, such as logging the error and returning null or throwing a custom
exception to indicate token parsing failure.
| private static final String SECRET_KEY = "fixlogfixlogfixlogfixlogfixlog1234"; // 32자 이상 | ||
| private static final long EXPIRATION_TIME = 1000 * 60 * 60 * 24; // 1일 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use configuration properties instead of hardcoded values
Hardcoding sensitive values like secret keys directly in source code is a security risk. Move these to configuration properties that can be externalized.
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.context.properties.ConfigurationProperties;
@Component
public class JwtTokenProvider {
- private static final String SECRET_KEY = "fixlogfixlogfixlogfixlogfixlog1234"; // 32자 이상
- private static final long EXPIRATION_TIME = 1000 * 60 * 60 * 24; // 1일
+ private final String secretKey;
+ private final long expirationTime;
+
+ public JwtTokenProvider(
+ @Value("${jwt.secret-key}") String secretKey,
+ @Value("${jwt.expiration-time:86400000}") long expirationTime) {
+ this.secretKey = secretKey;
+ this.expirationTime = expirationTime;
+ }Then in your application.properties or application.yml:
jwt:
secret-key: your-secure-key-here-at-least-32-characters
expiration-time: 86400000 # 1 day in milliseconds
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| private static final String SECRET_KEY = "fixlogfixlogfixlogfixlogfixlog1234"; // 32자 이상 | |
| private static final long EXPIRATION_TIME = 1000 * 60 * 60 * 24; // 1일 | |
| import org.springframework.beans.factory.annotation.Value; | |
| import org.springframework.boot.context.properties.ConfigurationProperties; | |
| @Component | |
| public class JwtTokenProvider { | |
| private final String secretKey; | |
| private final long expirationTime; | |
| public JwtTokenProvider( | |
| @Value("${jwt.secret-key}") String secretKey, | |
| @Value("${jwt.expiration-time:86400000}") long expirationTime) { | |
| this.secretKey = secretKey; | |
| this.expirationTime = expirationTime; | |
| } | |
| // existing methods... | |
| } |
🤖 Prompt for AI Agents
In src/main/java/com/example/fixlog/jwt/JwtTokenProvider.java around lines 12 to
13, the SECRET_KEY and EXPIRATION_TIME are hardcoded, which is a security risk.
Refactor the code to load these values from external configuration properties
(e.g., application.properties or application.yml) using Spring's @Value
annotation or a @ConfigurationProperties class. Remove the hardcoded constants
and inject the secret key and expiration time from the configuration to improve
security and flexibility.
| public boolean validateToken(String token) { | ||
| try { | ||
| Jwts.parserBuilder().setSigningKey(SECRET_KEY.getBytes()).build().parseClaimsJws(token); | ||
| return true; | ||
| } catch (JwtException e) { | ||
| return false; | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Enhance token validation with specific error handling and logging
The current token validation method doesn't distinguish between different types of JWT exceptions and doesn't provide logging. Improving this would make debugging easier.
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
@Component
public class JwtTokenProvider {
+ private static final Logger logger = LoggerFactory.getLogger(JwtTokenProvider.class);
// ...
public boolean validateToken(String token) {
try {
- Jwts.parserBuilder().setSigningKey(SECRET_KEY.getBytes()).build().parseClaimsJws(token);
+ Jwts.parserBuilder().setSigningKey(secretKey.getBytes()).build().parseClaimsJws(token);
return true;
- } catch (JwtException e) {
+ } catch (ExpiredJwtException e) {
+ logger.error("JWT token expired: {}", e.getMessage());
+ return false;
+ } catch (MalformedJwtException e) {
+ logger.error("Malformed JWT token: {}", e.getMessage());
+ return false;
+ } catch (UnsupportedJwtException e) {
+ logger.error("Unsupported JWT token: {}", e.getMessage());
+ return false;
+ } catch (IllegalArgumentException e) {
+ logger.error("JWT claims string is empty: {}", e.getMessage());
+ return false;
+ } catch (JwtException e) {
+ logger.error("JWT validation error: {}", e.getMessage());
return false;
}
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| public boolean validateToken(String token) { | |
| try { | |
| Jwts.parserBuilder().setSigningKey(SECRET_KEY.getBytes()).build().parseClaimsJws(token); | |
| return true; | |
| } catch (JwtException e) { | |
| return false; | |
| } | |
| } | |
| // At the top of JwtTokenProvider.java, add: | |
| import org.slf4j.Logger; | |
| import org.slf4j.LoggerFactory; | |
| @Component | |
| public class JwtTokenProvider { | |
| private static final Logger logger = LoggerFactory.getLogger(JwtTokenProvider.class); | |
| // ... | |
| public boolean validateToken(String token) { | |
| try { | |
| Jwts.parserBuilder() | |
| .setSigningKey(secretKey.getBytes()) | |
| .build() | |
| .parseClaimsJws(token); | |
| return true; | |
| } catch (ExpiredJwtException e) { | |
| logger.error("JWT token expired: {}", e.getMessage()); | |
| return false; | |
| } catch (MalformedJwtException e) { | |
| logger.error("Malformed JWT token: {}", e.getMessage()); | |
| return false; | |
| } catch (UnsupportedJwtException e) { | |
| logger.error("Unsupported JWT token: {}", e.getMessage()); | |
| return false; | |
| } catch (IllegalArgumentException e) { | |
| logger.error("JWT claims string is empty: {}", e.getMessage()); | |
| return false; | |
| } catch (JwtException e) { | |
| logger.error("JWT validation error: {}", e.getMessage()); | |
| return false; | |
| } | |
| } | |
| // ... | |
| } |
🤖 Prompt for AI Agents
In src/main/java/com/example/fixlog/jwt/JwtTokenProvider.java around lines 33 to
40, the validateToken method currently catches all JwtException types
generically and returns false without logging. Update the method to catch
specific JWT exceptions like ExpiredJwtException, MalformedJwtException, and
SignatureException separately, and add appropriate logging statements for each
case to improve error visibility and debugging.
| @Entity | ||
| @Getter | ||
| @NoArgsConstructor(access = AccessLevel.PROTECTED) | ||
| public class Post { | ||
|
|
||
| @Id | ||
| @GeneratedValue(strategy = GenerationType.IDENTITY) | ||
| @Column(name = "postId", nullable = false) | ||
| private Long id; | ||
|
|
||
| @Column(length = 20, nullable = false) | ||
| private String title; | ||
|
|
||
| @Column(columnDefinition = "TEXT", nullable = false) | ||
| private String content; | ||
|
|
||
| @ManyToOne(fetch = FetchType.LAZY) | ||
| @JoinColumn(name = "userId", nullable = false) | ||
| private Member member; | ||
|
|
||
| private LocalDateTime createdAt; | ||
| private LocalDateTime updatedAt; | ||
|
|
||
| @OneToMany(mappedBy = "post", cascade = CascadeType.ALL, orphanRemoval = true) | ||
| private List<PostImage> postImages = new ArrayList<>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add a constructor with required fields.
The entity only has a no-args constructor, making it less clear how to properly create a new Post instance. Consider adding a constructor that takes all required fields.
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Post {
// existing code...
+
+ public Post(String title, String content, Member member) {
+ this.title = title;
+ this.content = content;
+ this.member = member;
+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| @Entity | |
| @Getter | |
| @NoArgsConstructor(access = AccessLevel.PROTECTED) | |
| public class Post { | |
| @Id | |
| @GeneratedValue(strategy = GenerationType.IDENTITY) | |
| @Column(name = "postId", nullable = false) | |
| private Long id; | |
| @Column(length = 20, nullable = false) | |
| private String title; | |
| @Column(columnDefinition = "TEXT", nullable = false) | |
| private String content; | |
| @ManyToOne(fetch = FetchType.LAZY) | |
| @JoinColumn(name = "userId", nullable = false) | |
| private Member member; | |
| private LocalDateTime createdAt; | |
| private LocalDateTime updatedAt; | |
| @OneToMany(mappedBy = "post", cascade = CascadeType.ALL, orphanRemoval = true) | |
| private List<PostImage> postImages = new ArrayList<>(); | |
| @Entity | |
| @Getter | |
| @NoArgsConstructor(access = AccessLevel.PROTECTED) | |
| public class Post { | |
| @Id | |
| @GeneratedValue(strategy = GenerationType.IDENTITY) | |
| @Column(name = "postId", nullable = false) | |
| private Long id; | |
| @Column(length = 20, nullable = false) | |
| private String title; | |
| @Column(columnDefinition = "TEXT", nullable = false) | |
| private String content; | |
| @ManyToOne(fetch = FetchType.LAZY) | |
| @JoinColumn(name = "userId", nullable = false) | |
| private Member member; | |
| private LocalDateTime createdAt; | |
| private LocalDateTime updatedAt; | |
| @OneToMany(mappedBy = "post", cascade = CascadeType.ALL, orphanRemoval = true) | |
| private List<PostImage> postImages = new ArrayList<>(); | |
| public Post(String title, String content, Member member) { | |
| this.title = title; | |
| this.content = content; | |
| this.member = member; | |
| } | |
| } |
🤖 Prompt for AI Agents
In src/main/java/com/example/fixlog/domain/post/Post.java between lines 13 and
37, the Post entity currently only has a no-args constructor, which makes it
unclear how to instantiate with required fields. Add a constructor that accepts
all mandatory fields such as title, content, member, createdAt, and updatedAt,
and initializes the corresponding fields to ensure proper creation of Post
instances.
🛠️ Refactor suggestion
Add methods to manage bidirectional relationships.
The Post entity has a bidirectional relationship with PostImage, but lacks helper methods to properly manage this relationship. Consider adding methods to add and remove post images while maintaining both sides of the relationship.
@OneToMany(mappedBy = "post", cascade = CascadeType.ALL, orphanRemoval = true)
private List<PostImage> postImages = new ArrayList<>();
+
+public void addPostImage(PostImage postImage) {
+ postImages.add(postImage);
+ postImage.setPost(this);
+}
+
+public void removePostImage(PostImage postImage) {
+ postImages.remove(postImage);
+ postImage.setPost(null);
+}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| @Entity | |
| @Getter | |
| @NoArgsConstructor(access = AccessLevel.PROTECTED) | |
| public class Post { | |
| @Id | |
| @GeneratedValue(strategy = GenerationType.IDENTITY) | |
| @Column(name = "postId", nullable = false) | |
| private Long id; | |
| @Column(length = 20, nullable = false) | |
| private String title; | |
| @Column(columnDefinition = "TEXT", nullable = false) | |
| private String content; | |
| @ManyToOne(fetch = FetchType.LAZY) | |
| @JoinColumn(name = "userId", nullable = false) | |
| private Member member; | |
| private LocalDateTime createdAt; | |
| private LocalDateTime updatedAt; | |
| @OneToMany(mappedBy = "post", cascade = CascadeType.ALL, orphanRemoval = true) | |
| private List<PostImage> postImages = new ArrayList<>(); | |
| @Entity | |
| @Getter | |
| @NoArgsConstructor(access = AccessLevel.PROTECTED) | |
| public class Post { | |
| @Id | |
| @GeneratedValue(strategy = GenerationType.IDENTITY) | |
| @Column(name = "postId", nullable = false) | |
| private Long id; | |
| @Column(length = 20, nullable = false) | |
| private String title; | |
| @Column(columnDefinition = "TEXT", nullable = false) | |
| private String content; | |
| @ManyToOne(fetch = FetchType.LAZY) | |
| @JoinColumn(name = "userId", nullable = false) | |
| private Member member; | |
| private LocalDateTime createdAt; | |
| private LocalDateTime updatedAt; | |
| @OneToMany(mappedBy = "post", cascade = CascadeType.ALL, orphanRemoval = true) | |
| private List<PostImage> postImages = new ArrayList<>(); | |
| public void addPostImage(PostImage postImage) { | |
| postImages.add(postImage); | |
| postImage.setPost(this); | |
| } | |
| public void removePostImage(PostImage postImage) { | |
| postImages.remove(postImage); | |
| postImage.setPost(null); | |
| } | |
| } |
🤖 Prompt for AI Agents
In src/main/java/com/example/fixlog/domain/post/Post.java between lines 13 and
37, the Post entity has a bidirectional relationship with PostImage but lacks
helper methods to manage it. Add methods such as addPostImage(PostImage
postImage) and removePostImage(PostImage postImage) that update both the
postImages list in Post and set or clear the post reference in PostImage to keep
both sides synchronized.
| private LocalDateTime createdAt; | ||
| private LocalDateTime updatedAt; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add JPA auditing annotations for timestamp fields.
The timestamp fields createdAt and updatedAt are defined but don't have JPA auditing annotations to automatically populate them. Consider using @CreatedDate and @LastModifiedDate with @EntityListeners(AuditingEntityListener.class) to enable automatic timestamp management.
+import org.springframework.data.annotation.CreatedDate;
+import org.springframework.data.annotation.LastModifiedDate;
+import org.springframework.data.jpa.domain.support.AuditingEntityListener;
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
+@EntityListeners(AuditingEntityListener.class)
public class Post {
// ...
+ @CreatedDate
private LocalDateTime createdAt;
+ @LastModifiedDate
private LocalDateTime updatedAt;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| private LocalDateTime createdAt; | |
| private LocalDateTime updatedAt; | |
| // add these imports | |
| import org.springframework.data.annotation.CreatedDate; | |
| import org.springframework.data.annotation.LastModifiedDate; | |
| import org.springframework.data.jpa.domain.support.AuditingEntityListener; | |
| @Entity | |
| @Getter | |
| @NoArgsConstructor(access = AccessLevel.PROTECTED) | |
| @EntityListeners(AuditingEntityListener.class) | |
| public class Post { | |
| // ... | |
| @CreatedDate | |
| private LocalDateTime createdAt; | |
| @LastModifiedDate | |
| private LocalDateTime updatedAt; | |
| // ... | |
| } |
🤖 Prompt for AI Agents
In src/main/java/com/example/fixlog/domain/post/Post.java around lines 33 to 34,
the fields createdAt and updatedAt lack JPA auditing annotations. Add
@CreatedDate to createdAt and @LastModifiedDate to updatedAt, and annotate the
entity class with @EntityListeners(AuditingEntityListener.class) to enable
automatic timestamp population by JPA auditing.
|
확인했습니다! |
|
아예 새로 pr 을 날리는 게 좋을 거 같아서 pr 닫겠습니다! |
이슈 번호
작업 내용
기타
jwt 토큰을 활용한 로그인&회원 탈퇴 기능 구현 예정
Summary by CodeRabbit
New Features
Bug Fixes
Chores