NBTI는 현대인의 인지 역량을 쉽고 정확하게 측정하고,
개인화된 루틴 트레이닝을 통해 생각의 힘을 키워가는 데이터 기반 두뇌 성장 플랫폼

🧐 프로젝트 배경
최근 한국인들의 사고능력이 떨어지고 있다. 정보 찾기는 쉬워졌지만, 스스로 생각하는 힘은 서서히 사라지고 있다. 이런 상황을 보며 생각하는 힘을 기를 수 있는 사이트를 만들면 어떨까 생각하게 됐다.
프로젝트 주요 기능은 크게 사용자 중심의 기능과 관리자 기능 2가지로 분류되어 있고, 각각 간단하게 어떤 기능이 있었는지 살펴보면 다음과 같다.
(1) 사용자 기능
: 정식 검사 / 맛보기 검사, AI 분석 리포트, 학습 기능, 이의 제기 기능, 마이페이지 기록 관리
(2) 관리자 기능
: 문제 관리 페이지, 검사 이력 관리, 학습 내역 관리, 검색 및 정렬 고도화, 분야/유형 관리 페이지
우리의 시스템 아키텍처는 다음과 같다. 자, 이제 지금부터 아키텍처의 흐름에 대한 설명과 각각의 과정에서 사용된 기술 스택을 이야기 하려고 한다.
(1) 데이터베이스 & 캐시
(2) 백엔드
(3) 프론트엔드
(4) 컨테이너 & 배포
(5) CI/CD 파이프라인
(6) 모니터링 & 로깅
Kibana + Elasticsearch + Logstash : 로그 수집 용도로 활용
프로젝트는 다음과 같은 순서로 진행됐다.
기획 → DDD → 요구사항 명세서 작성 → 설계 (ERD, DB, UI) → 백엔드 개발 → 프론트엔드 개발 → 데브옵스 설정 → 프로젝트 시연
프로젝트를 진행할 때마다 다양한 고민을 하게 된다. 이 프로젝트에서는 다음과 같은 부분을 팀원들과 논의했다.
(1) 팀 컨벤션 정의
(2) 아키텍처 및 패턴 선정
(1) NBTI 정식 검사 및 맛보기 검사 기능 구현
(2) Docker Compose 작성 및 도커 환경 구축
(3) 시연 준비 및 진행
해당 부분은 내가 개발한 부분이다. 문제를 풀로 그 결과까지 도출되는 모습을 보여주는 화면이다. 마지막에 나오는 AI 총평 부분이 Open AI에서 분석해준 결과이다.

(1) 기능과 관련된 고민
이 부분은 트러블 슈팅이라기 보단, 개발을 진행하면서 좀 더 좋은 방향으로 가기 위해 고민한 것들을 이야기 할 것이다.
‼️회원/비회원 차등 기능 제공 과정에서 문항 부족 문제를 발견
이번 프로젝트에서 우리 팀이 직접 문제를 제작하거나 가져오면서 문제를 선정했다. 그렇기 때문에 문제를 무제한으로 제공할 수 없었다. 그래서 어떻게 해야 문제를 적게 제공할 수 있을지에 대한 고민을 했다. 그 결과 다음과 같이 프로젝트에 적용했다.
18문항의 정식 검사, 비회원은 6문항 맛보기 검사포인트 시스템을 도입해 회원만 정식 검사를 이용할 수 있도록 변경 이외에도 "문항은 몇 개가 적절한지?", "비회원에게 몇 점짜리 문항을 제공하는 게 적절한지?" 등 여러 가지를 고민했다.
(2) @RequiredArgsConstructor 문제
❌ 문제 상황
코드를 밑에 처럼 작성했는데, 계속 에러가 발생
@Service
@RequiredArgsConstructor
public class TestAiAnswerServiceImpl implements TestAiAnswerService {
private final OpenAIClient client;
public TestAiAnswerServiceImpl(@Value("${openai.api-key}") String apiKey) {
this.client = OpenAIOkHttpClient.builder()
.apiKey(apiKey)
.build();
}
}
위에 처럼 @RequiredArgsConstructor와 수동 생성자를 함께 정의하면 충돌이 발생하게 된다.
@RequiredArgsConstructor가 생성한 자동 생성자
public TestAiAnswerServiceImpl(OpenAIClient client) {
this.client = client;
}
내가 직접 생성한 생성자
public TestAiAnswerServiceImpl(@Value("${openai.api-key}") String apiKey) {
this.client = OpenAIOkHttpClient.builder()
.apiKey(apiKey)
.build();
}
결과적으로 Bean 자체 등록이 되지 않으면서 오류가 발생한 것이다.
✅ 문제 해결
@RequiredArgsConstructor 제거
→ 직접 생성자로 의존 객체를 만드는 방식을 사용
→ 이 클래스는 비즈니스 로직이 있는 게 아니라 설정을 관리하는 로직이기 때문에 @Service 어노테이션을 이용해 제작하는 것이 어색함.
그렇기 때문에, 추후 밑에와 같이 서비스 로직과 인프라 로직을 명확하게 분리해서 사용하는 방식으로 발전해야 한다.
@Configuration
public class OpenAIConfig {
@Value("${openai.api-key}")
private String apiKey;
@Bean
public OpenAIClient openAIClient() {
return OpenAIOkHttpClient.builder()
.apiKey(apiKey)
.build();
}
}
그래서 이렇게 AI를 연결하는 로직은 설정에서 만들고, 서비스 로직에서 주입 받고 사용하는 것이 좋다.
(실제로 이후 API를 연결해 진행하는 프로젝트에서는 다음과 같은 방식을 적용함)
이번 프로젝트를 진행하면서 docker, k8s, jenkins 등 컨테이너 배포 환경과 CI/CD를 구축하는 방법과 과정에 부족함이 많은것을 깨달았다. 물론, 설정에 필요한 모든 것을 외울 필요는 없지만 어떤 과정으로 진행되고 어떤 설정을 하고 있는지에 대한 인식은 확실하게 필요한 것 같다. 그래서 이 부분에 대한 보충 공부를 많이 해야 할 것 같다.
지난 프로젝트 까지는 백엔드와 프론트엔드를 어떻게 하는지 몰라서 많이 힘들었는데, 그래도 이번 프로젝트에서는 감이 잡혀서 여러 요소를 고민할 수 있는 프로젝트였다.
프로젝트 회고 정리하고 계시군요 ㅎㅎ 잘 보고 갑니다.
1-1 중 프로젝트 배경에 오타가 하나 있네요. (따 -> 다)