Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import com.back.domain.comment.dto.CommentRequest;
import com.back.domain.comment.dto.CommentResponse;
import com.back.domain.comment.entity.Comment;
import com.back.domain.comment.enums.CommentSortType;
import com.back.domain.comment.mapper.CommentMappers;
import com.back.domain.comment.repository.CommentRepository;
import com.back.domain.post.entity.Post;
Expand All @@ -12,12 +11,9 @@
import com.back.domain.user.repository.UserRepository;
import com.back.global.exception.ApiException;
import com.back.global.exception.ErrorCode;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

Expand Down Expand Up @@ -60,6 +56,7 @@ public Long updateComment(Long userId, Long commentId, CommentRequest request) {
return comment.getId();
}

@Transactional
public void deleteComment(Long userId, Long commentId) {
Comment comment = commentRepository.findById(commentId)
.orElseThrow(() -> new ApiException(ErrorCode.COMMENT_NOT_FOUND));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ public ResponseEntity<BaseNodeDto> getBaseNode(@PathVariable Long baseNodeId) {
// 사용자 전체 트리 조회 (베이스/결정 노드 일괄 반환)
@GetMapping("/{baseLineId}/tree")
public ResponseEntity<TreeDto> getTreeForBaseLine(@PathVariable Long baseLineId) {
// 트리 조회 서비스 호출
TreeDto tree = nodeService.getTreeForBaseLine(baseLineId);
return ResponseEntity.ok(tree);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@
@Repository
public interface BaseLineRepository extends JpaRepository<BaseLine, Long> {
Optional<BaseLine> findByUser(User user);
long countByUser(User user); // 기본 인덱스 계산용
long countByUser(User user);
// Guest 베이스라인 1개 제한 확인용
boolean existsByUser_id(Long userId);

boolean existsByUserAndTitle(User user, String title); // 충돌 회피용
boolean existsByUserAndTitle(User user, String title);
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import com.back.domain.node.mapper.NodeMappers;
import com.back.domain.node.repository.BaseLineRepository;
import com.back.domain.node.repository.BaseNodeRepository;
import com.back.domain.user.entity.Role;
import com.back.domain.user.entity.User;
import com.back.domain.user.repository.UserRepository;
import com.back.global.exception.ApiException;
Expand All @@ -35,9 +36,15 @@ class BaseLineService {

// 노드 일괄 생성(save chain)
public BaseLineBulkCreateResponse createBaseLineWithNodes(BaseLineBulkCreateRequest request) {

support.validateBulkRequest(request);
User user = userRepository.findById(request.userId())
.orElseThrow(() -> new ApiException(ErrorCode.USER_NOT_FOUND, "User not found: " + request.userId()));

// Guest는 베이스라인 1개 제한
if (user.getRole() == Role.GUEST && baseLineRepository.existsByUser_id(user.getId())) {
throw new ApiException(ErrorCode.GUEST_BASELINE_LIMIT, "Guest user can have only one baseline.");
}
String title = support.normalizeOrAutoTitle(request.title(), user);

BaseLine baseLine = baseLineRepository.save(BaseLine.builder().user(user).title(title).build());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public enum ErrorCode {
NODE_NOT_FOUND(HttpStatus.NOT_FOUND, "N001", "Node Not Found"),
BASE_LINE_NOT_FOUND(HttpStatus.NOT_FOUND, "N002", "BaseLine Not Found"),
DECISION_LINE_NOT_FOUND(HttpStatus.NOT_FOUND, "N003", "DecisionLine Not Found"),
GUEST_BASELINE_LIMIT(HttpStatus.BAD_REQUEST, "N004" , "Guest Base Line Limit Exceeded"),

// Scenario Errors
SCENARIO_NOT_FOUND(HttpStatus.NOT_FOUND, "SC001", "Scenario Not Found"),
Expand Down
153 changes: 129 additions & 24 deletions back/src/main/java/com/back/global/initdata/InitData.java
Original file line number Diff line number Diff line change
@@ -1,33 +1,39 @@
package com.back.global.initdata;

import com.back.domain.user.entity.Gender;
import com.back.domain.user.entity.Mbti;
import com.back.domain.user.entity.Role;
import com.back.domain.user.entity.User;
import com.back.domain.node.dto.PivotListDto;
import com.back.domain.node.dto.base.BaseLineBulkCreateRequest;
import com.back.domain.node.dto.base.BaseLineBulkCreateResponse;
import com.back.domain.node.dto.decision.DecNodeDto;
import com.back.domain.node.dto.decision.DecisionNodeFromBaseRequest;
import com.back.domain.node.dto.decision.DecisionNodeNextRequest;
import com.back.domain.node.entity.NodeCategory;
import com.back.domain.node.service.NodeService;
import com.back.domain.user.entity.*;
import com.back.domain.user.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.CommandLineRunner;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.List;

/**
* 애플리케이션 시작 시 초기 데이터를 생성하는 컴포넌트.
* 개발 환경에서 필요한 기본 사용자(관리자, 일반 사용자)를 데이터베이스에 저장합니다.
* [요약] 기동 시 admin·user1 생성 → user1에 베이스라인(총7: 헤더+피벗5+테일) 1개와 결정라인(총5 노드) 1개 시드 주입.
*/
@Component
@RequiredArgsConstructor
public class InitData implements CommandLineRunner {

private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
private final NodeService nodeService;

// user1을 만들고 베이스라인(7)과 결정라인(5)을 시드로 주입한다
@Override
public void run(String... args) throws Exception {
// 애플리케이션 시작 시 초기 사용자 데이터 생성
public void run(String... args) {
if (userRepository.findByEmail("[email protected]").isEmpty()) {
User admin = User.builder()
var admin = User.builder()
.email("[email protected]")
.password(passwordEncoder.encode("admin1234!"))
.role(Role.ADMIN)
Expand All @@ -41,19 +47,118 @@ public void run(String... args) throws Exception {
userRepository.save(admin);
}

if (userRepository.findByEmail("[email protected]").isEmpty()) {
User user1 = User.builder()
.email("[email protected]")
.password(passwordEncoder.encode("user1234!"))
.role(Role.USER)
.username("사용자1")
.nickname("사용자닉네임")
.birthdayAt(LocalDateTime.of(1995, 5, 10, 0, 0))
.gender(Gender.F)
.mbti(Mbti.ENFP)
.beliefs("개인주의")
.build();
userRepository.save(user1);
}
var user1 = userRepository.findByEmail("[email protected]")
.orElseGet(() -> userRepository.save(
User.builder()
.email("[email protected]")
.password(passwordEncoder.encode("user1234!"))
.role(Role.USER)
.username("사용자1")
.nickname("사용자닉네임")
.birthdayAt(LocalDateTime.of(1995, 5, 10, 0, 0))
.gender(Gender.F)
.mbti(Mbti.ENFP)
.beliefs("개인주의")
.build()
));

BaseLineBulkCreateResponse baseRes = nodeService.createBaseLineWithNodes(
new BaseLineBulkCreateRequest(
user1.getId(),
"user1-기본 라인",
List.of(
new BaseLineBulkCreateRequest.BaseNodePayload(
NodeCategory.EDUCATION, "중학교 진학", "일반계 선택", 18, "중등 입학 및 진로 탐색 시작"
),
new BaseLineBulkCreateRequest.BaseNodePayload(
NodeCategory.EDUCATION, "고교 진학", "이과 트랙", 20, "수학·물리 집중 선택"
),
new BaseLineBulkCreateRequest.BaseNodePayload(
NodeCategory.EDUCATION, "대학 합격", "컴공 전공", 22, "알고리즘/네트워크 관심"
),
new BaseLineBulkCreateRequest.BaseNodePayload(
NodeCategory.CAREER, "인턴 경험", "백엔드 인턴", 24, "스프링 부트 실무 체험"
),
new BaseLineBulkCreateRequest.BaseNodePayload(
NodeCategory.CAREER, "첫 직장", "주니어 백엔드", 26, "API/DB 설계 중심"
)
)
)
);

Long baseLineId = baseRes.baseLineId();
PivotListDto pivots = nodeService.getPivotBaseNodes(baseLineId);
if (pivots.pivots() == null || pivots.pivots().isEmpty()) return;

DecNodeDto d0 = nodeService.createDecisionNodeFromBase(
new DecisionNodeFromBaseRequest(
user1.getId(),
baseLineId,
0,
null,
0,
NodeCategory.CAREER,
"개발자 커리어 진입",
List.of("자바/스프링", "파이썬/데이터"),
0,
"백엔드 중심 트랙을 초기 선택지로 제시"
)
);

DecNodeDto d1 = nodeService.createDecisionNodeNext(
new DecisionNodeNextRequest(
user1.getId(),
d0.id(),
NodeCategory.CAREER,
"클라우드 기초",
null,
List.of("AWS 기초", "GCP 기초"),
0,
0,
"EC2/RDS·CI/CD 파이프라인 구축"
)
);

DecNodeDto d2 = nodeService.createDecisionNodeNext(
new DecisionNodeNextRequest(
user1.getId(),
d1.id(),
NodeCategory.CAREER,
"보안 기초",
null,
List.of("웹 보안", "네트워크 보안"),
0,
0,
"JWT·세션·CSRF/XSS 대응 심화"
)
);

DecNodeDto d3 = nodeService.createDecisionNodeNext(
new DecisionNodeNextRequest(
user1.getId(),
d2.id(),
NodeCategory.CAREER,
"대용량 처리",
null,
List.of("캐시·큐", "검색"),
0,
0,
"Redis·Kafka·Elasticsearch 실습"
)
);

nodeService.createDecisionNodeNext(
new DecisionNodeNextRequest(
user1.getId(),
d3.id(),
NodeCategory.CAREER,
"운영/관측성",
null,
List.of("로그·모니터링", "SLO/알림"),
0,
0,
"프로덕션 운영 지표와 알림 체계 정착"
)
);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import com.back.domain.user.repository.UserRepository;
import com.back.global.security.CustomUserDetails;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.persistence.EntityManager;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
Expand All @@ -23,8 +24,9 @@
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.jdbc.Sql;
import org.springframework.test.context.jdbc.SqlConfig;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Field;
Expand All @@ -40,20 +42,36 @@
@ActiveProfiles("test")
@SpringBootTest
@AutoConfigureMockMvc(addFilters = false)
@Transactional
@SqlConfig(transactionMode = SqlConfig.TransactionMode.ISOLATED)
@Sql(
statements = {
"SET REFERENTIAL_INTEGRITY FALSE",
"TRUNCATE TABLE COMMENTS",
"TRUNCATE TABLE POST",
"TRUNCATE TABLE USERS",

"ALTER TABLE COMMENTS ALTER COLUMN id RESTART WITH 1",
"ALTER TABLE POST ALTER COLUMN id RESTART WITH 1",
"ALTER TABLE USERS ALTER COLUMN id RESTART WITH 1",
"SET REFERENTIAL_INTEGRITY TRUE"
},
executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD
)
class CommentControllerTest {

@Autowired private UserRepository userRepository;
@Autowired private PostRepository postRepository;
@Autowired private CommentRepository commentRepository;
@Autowired private MockMvc mockMvc;
@Autowired private ObjectMapper objectMapper;
@Autowired private EntityManager em;

private User testUser;
private User anotherUser;
private Post testPost;
private Comment testComment;


@BeforeEach
void setUp() {
String uid1 = UUID.randomUUID().toString().substring(0, 5);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.jdbc.Sql;
import org.springframework.test.context.jdbc.SqlConfig;
import org.springframework.test.web.servlet.MockMvc;

import java.time.LocalDateTime;
Expand All @@ -35,6 +37,25 @@
@AutoConfigureMockMvc(addFilters = false)
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@DisplayName("Re:Life — BaseLine/BaseNode 통합 테스트")
@SqlConfig(transactionMode = SqlConfig.TransactionMode.ISOLATED)
@Sql(
statements = {
"SET REFERENTIAL_INTEGRITY FALSE",
"TRUNCATE TABLE DECISION_NODES",
"TRUNCATE TABLE DECISION_LINES",
"TRUNCATE TABLE BASE_NODES",
"TRUNCATE TABLE BASE_LINES",
"TRUNCATE TABLE USERS",

"ALTER TABLE DECISION_NODES ALTER COLUMN ID RESTART WITH 1",
"ALTER TABLE DECISION_LINES ALTER COLUMN ID RESTART WITH 1",
"ALTER TABLE BASE_NODES ALTER COLUMN ID RESTART WITH 1",
"ALTER TABLE BASE_LINES ALTER COLUMN ID RESTART WITH 1",
"ALTER TABLE USERS ALTER COLUMN ID RESTART WITH 1",
"SET REFERENTIAL_INTEGRITY TRUE"
},
executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD
)
public class BaseLineControllerTest {

@Autowired private MockMvc mockMvc;
Expand All @@ -46,17 +67,17 @@ public class BaseLineControllerTest {

private Long userId;

@BeforeAll
@BeforeEach
void initUser() {
String uid = UUID.randomUUID().toString().substring(0, 8);
User user = User.builder()
.email("user_" + uid + "@test.local")
.role(Role.GUEST)
.role(Role.USER)
.birthdayAt(LocalDateTime.now().minusYears(25))
.gender(Gender.M)
.mbti(Mbti.INTJ)
.beliefs("NONE")
.authProvider(AuthProvider.GUEST)
.authProvider(AuthProvider.LOCAL)
.nickname("tester-" + uid)
.username("name-" + uid)
.build();
Expand Down
Loading