Skip to content
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

[REFACTOR] UserService 구현 포함, 전체 코드 리팩토링 #180

Merged
merged 21 commits into from
Oct 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
9ca26e1
:recycle: docker compose 테스트 DB 설정 제거
kimminkyeu Oct 8, 2024
8902f53
Merge remote-tracking branch 'refs/remotes/origin/backend' into back/…
kimminkyeu Oct 8, 2024
05b2def
Merge remote-tracking branch 'refs/remotes/origin/backend' into back/…
kimminkyeu Oct 9, 2024
92bc750
Merge remote-tracking branch 'refs/remotes/origin/backend' into back/…
kimminkyeu Oct 9, 2024
75b21c8
:sparkles: feat: UserService 기능 구현
kimminkyeu Oct 9, 2024
c49e271
:recycle: refactor: Tag Feature 리팩토링 (의존성 제거)
kimminkyeu Oct 9, 2024
25e1735
:recycle: refactor: Pick 도메인 리팩토링
kimminkyeu Oct 9, 2024
a15c914
:recycle: refactor: Structure 도메인 리팩토링
kimminkyeu Oct 9, 2024
58a413c
:recycle: refactor: Link 도메인 리팩토링
kimminkyeu Oct 9, 2024
0db8e24
:recycle: refactor: Folder 도메인 리팩토링
kimminkyeu Oct 9, 2024
3432868
:recycle: refactor: FolderStructure 도메인 리펙토링
kimminkyeu Oct 9, 2024
8da4f56
:recycle: refactor: FolderStructure 도메인 리펙토링 (기존 코드 삭제)
kimminkyeu Oct 9, 2024
d949c16
:recycle: refactor: OAuth 도메인 리펙토링
kimminkyeu Oct 9, 2024
e034c49
:recycle: refactor: User 이름 랜덤 생성기 추가
kimminkyeu Oct 9, 2024
2b55700
:recycle: refactor: Folder 예외 추가
kimminkyeu Oct 9, 2024
946949b
:recycle: refactor: Parser 테스트 주석 처리 (provider 의존성 필요)
kimminkyeu Oct 9, 2024
0566ca7
:recycle: refactor: 주석 오류 수정
kimminkyeu Oct 9, 2024
7909ba1
:recycle: refactor: 테스트 코드 준비
kimminkyeu Oct 9, 2024
cc07097
:recycle: refactor: 코드 리뷰 피드백 반영
kimminkyeu Oct 10, 2024
4319206
Merge remote-tracking branch 'refs/remotes/origin/backend' into back/…
kimminkyeu Oct 10, 2024
9d6d458
:recycle: refactor: 코드 리뷰 피드백 반영
kimminkyeu Oct 10, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 20 additions & 20 deletions backend/docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,26 +33,26 @@ services:
networks:
- techpick-network

techpick-mysql-test:
image: mysql:8.0
container_name: techpick-mysql-test
ports:
- "3307:3306"
env_file:
- .env
environment:
- MYSQL_ROOT_PASSWORD=${DOCKER_MYSQL_TEST_PASSWORD}
- MYSQL_DATABASE=${DOCKER_MYSQL_TEST_DATABASE}
- TZ=Asia/Seoul
volumes:
- ./data/test/mysql-data:/var/lib/mysql
- ./data/test/mysql-files:/var/lib/mysql-files/test
command:
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_unicode_ci
- --lower_case_table_names=1
networks:
- techpick-network
# techpick-mysql-test:
# image: mysql:8.0
# container_name: techpick-mysql-test
# ports:
# - "3307:3306"
# env_file:
# - .env
# environment:
# - MYSQL_ROOT_PASSWORD=${DOCKER_MYSQL_TEST_PASSWORD}
# - MYSQL_DATABASE=${DOCKER_MYSQL_TEST_DATABASE}
# - TZ=Asia/Seoul
# volumes:
# - ./data/test/mysql-data:/var/lib/mysql
# - ./data/test/mysql-files:/var/lib/mysql-files/test
# command:
# - --character-set-server=utf8mb4
# - --collation-server=utf8mb4_unicode_ci
# - --lower_case_table_names=1
# networks:
# - techpick-network
# 로컬 테스트 용 ( 아래 설정은 실제 서버에 올라간 Compose 와 다름 )
# techpick-server:
# build:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,15 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter {
private static final String ACCESS_TOKEN_KEY = "access_token";

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
protected void doFilterInternal(
HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain
) throws ServletException, IOException {
String token = getTokenFromCookie(request);

if (jwtUtil.isValidToken(token)) {
var authentication = convertToAuthentication(token);
Authentication authentication = convertToAuthentication(token);
SecurityContextHolder.getContext().setAuthentication(authentication);
} else {
// 인증 실패시 techPickLogin 쿠키 삭제
Expand All @@ -45,7 +48,7 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse
}

private String getTokenFromCookie(HttpServletRequest request) {
var cookies = request.getCookies();
Cookie[] cookies = request.getCookies();
if (cookies == null) {
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
import kernel360.techpick.core.model.user.User;
import kernel360.techpick.core.util.CookieUtil;
import kernel360.techpick.core.util.JwtUtil;
import kernel360.techpick.feature.user.UserRepository;
import kernel360.techpick.feature.user.repository.UserRepository;
import lombok.RequiredArgsConstructor;

@Component
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,34 +13,33 @@

import kernel360.techpick.core.auth.model.OAuth2UserInfo;
import kernel360.techpick.core.config.OAuth2AttributeConfigProvider;
import kernel360.techpick.core.model.folder.Folder;
import kernel360.techpick.core.model.folder.FolderType;
import kernel360.techpick.core.model.folder.StructureJson;
import kernel360.techpick.core.model.user.User;
import kernel360.techpick.feature.folder.repository.FolderRepository;
import kernel360.techpick.feature.structure.repository.StructureJsonRepository;
import kernel360.techpick.feature.user.UserRepository;
import kernel360.techpick.feature.user.service.UserService;
import kernel360.techpick.feature.user.service.dto.SocialUserCreateDto;
import lombok.RequiredArgsConstructor;

@Service
@RequiredArgsConstructor
public class CustomOAuth2Service extends DefaultOAuth2UserService {

private final UserRepository userRepository;
private final FolderRepository folderRepository;
private final StructureJsonRepository structureJsonRepository;
private final UserService userService;
private final OAuth2AttributeConfigProvider configProvider;

@Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
String provider = userRequest.getClientRegistration().getRegistrationId();
var oAuth2User = super.loadUser(userRequest);
Map<String, Object> attributes = getAttributes(oAuth2User, provider);
var oAuth2UserInfo = new OAuth2UserInfo(provider, attributes);

if (!userRepository.existsBySocialProviderId(oAuth2UserInfo.getName())) {
User user = saveOAuth2UserInfo(oAuth2UserInfo);
createBasicFolder(user);
OAuth2UserInfo oAuth2UserInfo = new OAuth2UserInfo(provider, attributes);

if (!userService.isUserExistsBySocialProviderId(oAuth2UserInfo.getName())) {
userService.createNewSocialUser(
new SocialUserCreateDto(
oAuth2UserInfo.getProvider(),
oAuth2UserInfo.getName(), // providerId = oAuthUserInfo's name
oAuth2UserInfo.getEmail()
)
);
}
return oAuth2UserInfo;
}
Expand Down Expand Up @@ -74,31 +73,4 @@ private Object searchAttribute(String targetKey, Map<String, Object> map) {
// TODO: ApiUserException 으로 리팩토링 예정
throw new IllegalArgumentException("Attribute of " + targetKey + " is not found");
}

private User saveOAuth2UserInfo(OAuth2UserInfo oAuth2UserInfo) {
User user = User.create(
oAuth2UserInfo.getProvider(),
oAuth2UserInfo.getName(),
oAuth2UserInfo.getEmail()
);
return userRepository.save(user);
}

private void createBasicFolder(User user) {

folderRepository.save(Folder.create("미분류폴더", FolderType.UNCLASSIFIED, user));
folderRepository.save(Folder.create("휴지통", FolderType.RECYCLE_BIN, user));
folderRepository.save(Folder.create("최상위폴더", FolderType.ROOT, user));

StructureJson json = StructureJson.create(
"""
{
"root": [],
"recycleBin": []
}
""",
user
);
structureJsonRepository.save(json);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package kernel360.techpick.core.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

@Configuration
public class PasswordEncoderConfig {
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public class Folder extends TimeTracking {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;

@Column(name = "name", nullable = false)
Expand Down Expand Up @@ -71,7 +72,19 @@ public Long findParentFolderId() {
}

// TODO: 엔티티 사용자가 정적 팩토리 메소드로 필요한 함수를 구현 하세요
public static Folder create(String name, FolderType folderType, User user) {
return new Folder(name, folderType, null, user);
public static Folder generalFolder(String name, User user) {
return new Folder(name, FolderType.GENERAL, null, user);
}

public static Folder rootFolder(String name, User user) {
return new Folder(name, FolderType.ROOT, null, user);
}

public static Folder recycleBinFolder(String name, User user) {
return new Folder(name, FolderType.RECYCLE_BIN, null, user);
}

public static Folder unclassifedFolder(String name, User user) {
return new Folder(name, FolderType.UNCLASSIFIED, null, user);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,22 @@

public enum FolderType {

UNCLASSIFIED,
RECYCLE_BIN,
GENERAL,
ROOT,

UNCLASSIFIED ("미분류 폴더"),
RECYCLE_BIN ("휴지통 폴더"),
ROOT ("루트 폴더"),
GENERAL ("일반 폴더"),
;

private final String label;

FolderType(String label) {
this.label = label;
}

public String getLabel() {
return label;
}

public static EnumSet<FolderType> getBasicFolderTypes() {
return EnumSet.of(UNCLASSIFIED, RECYCLE_BIN, ROOT);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package kernel360.techpick.core.model.folder;

import org.hibernate.annotations.JdbcTypeCode;
import org.hibernate.type.SqlTypes;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
Expand All @@ -11,6 +14,8 @@
import jakarta.persistence.Table;
import kernel360.techpick.core.model.common.TimeTracking;
import kernel360.techpick.core.model.user.User;
import kernel360.techpick.feature.structure.service.Structure;
import kernel360.techpick.feature.structure.service.node.server.ServerNode;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
Expand All @@ -23,26 +28,28 @@ public class StructureJson extends TimeTracking {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;

@JdbcTypeCode(SqlTypes.JSON)
@Column(columnDefinition = "longblob", nullable = false)
private String structure;
private Structure<ServerNode> structure;
kimminkyeu marked this conversation as resolved.
Show resolved Hide resolved

@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;

private StructureJson(String structure, User user) {
this.structure = structure;
private StructureJson(User user, Structure<ServerNode> structure) {
this.user = user;
this.structure = structure;
}

public void updateStructure(String structure) {
public void updateStructure(Structure<ServerNode> structure) {
this.structure = structure;
}

// TODO: 엔티티 사용자가 정적 팩토리 메소드로 필요한 함수를 구현 하세요
public static StructureJson create(String structure, User user) {
return new StructureJson(structure, user);
public static StructureJson create(User user, Structure<ServerNode> structure) {
return new StructureJson(user, structure);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public class Tag {

// order로 하니까 db 키워드랑 겹쳐 안됨..
@Column(name = "tag_order", nullable = false)
private int tagOrder;
private Integer tagOrder;

// 사용자 FK
@ManyToOne(fetch = FetchType.LAZY)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ public enum SocialType {
GOOGLE("google"),
KAKAO("kakao"),
NAVER("naver"),

;

private final String providerId;
Expand Down
28 changes: 23 additions & 5 deletions backend/src/main/java/kernel360/techpick/core/model/user/User.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package kernel360.techpick.core.model.user;

import java.time.LocalDateTime;
import java.util.Objects;

import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.SQLRestriction;
Expand Down Expand Up @@ -29,13 +30,14 @@
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class User extends TimeTracking /* implements UserDetails --> 시큐리티 도입시 추가 */ {

private static final String SOCIAL_USER_HAS_NO_PASSWORD = null;

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;

// 닉네임 (없으면 랜덤 생성 - Ex. "노래하는피치#145")
// TODO: 닉네임을 unique로 잡을지 토의 후 결정 (사용자 검색 시 이걸 id로 사용)
@Column(name = "nickname", nullable = false /*, unique = true */)
private String nickname;
Comment on lines 41 to 42
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p3: 사용자 검색 시 id로 이 컬럼을 사용한다면 unique index 거는 것이 좋다고 생각함.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

현재 닉네임은 중복이 발생함. 나중에 중복 발생 안되는 로직이 생기면 그때 유니크 거는게 좋다고 봄.


Expand All @@ -57,7 +59,7 @@ public class User extends TimeTracking /* implements UserDetails --> 시큐리
@Column(name = "social_provider") // nullable
private SocialType socialProvider;

// 소셜 제공자 id
// 소셜 제공자 Id
@Column(name = "social_provider_id") // nullable
private String socialProviderId;

Expand All @@ -80,12 +82,12 @@ public class User extends TimeTracking /* implements UserDetails --> 시큐리
private JobGroup jobGroup;

// TODO: 엔티티 사용자가 정적 팩토리 메소드로 필요한 함수를 구현 하세요
public static User create(SocialType provider, String providerId, String email) {
public static User basicSocialUser(SocialType provider, String providerId, String nickname, String email) {
return new User(
provider,
providerId,
"대충랜덤닉네임1", // TODO: 이후 랜덤닉네임 생성기를 통해 생성하도록 리팩토링 필요
null,
nickname,
SOCIAL_USER_HAS_NO_PASSWORD,
email,
Role.ROLE_USER
);
Expand All @@ -106,4 +108,20 @@ private User(
this.email = email;
this.role = role;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof User user)) {
return false;
}
return Objects.equals(id, user.id);
}

@Override
public int hashCode() {
return Objects.hashCode(id);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public static void addCookie(HttpServletResponse response, String name, String v
.path("/")
.httpOnly(true)
.secure(true)
.domain(".minlife.me")
// .domain(".minlife.me")
// .sameSite("None")
.build();
response.addHeader("Set-Cookie", responseCookie.toString());
Expand All @@ -28,7 +28,7 @@ public static void addCookie(HttpServletResponse response, String name, String v
.maxAge(maxAge)
.path("/")
.secure(true)
.domain(".minlife.me")
// .domain(".minlife.me")
// .sameSite("None")
.build();
response.addHeader("Set-Cookie", techPickLoginCookie.toString());
Expand Down
Loading
Loading