Experience/LG CNS AM Inspire Camp 1기

[LG CNS AM Inspire Camp] 28. 미니프로젝트 1 - 과거의 오늘 날짜 Hot news를 보여드립니다. newspace

chillmyh 2025. 5. 15. 01:18

1. 들어가며

2월 21일부터 2월 26일까지 진행했던 미니프로젝트인데 뒤늦게 정리 겸 회고글을 작성해본다. 시기 상 JavaScript, React, Java, Spring 에 대한 수업이 끝나고 조 편성이 되어 진행했었다. 주어진 프로젝트 요구 사항 중 주제는 AI를 활용한 뉴스 서비스 개발이었다.

 LG CNS AM Inspire Camp에 들어오기 전 소개글 중 AI를 다룬 다는 내용을 보고 Spring AI에 대한 관심이 있어 써보고 싶은 마음이 컸는데, 이 프로젝트에서 써보는 것으로 첫 경험을 쌓았었더랬다.

 팀원 구성 및 역할 분담은 프론트엔드 2명, 백엔드 2명, 인프라 1명이었다. 나는 여기서 팀장과 백엔드를 맡았고, Spring AI를 활용한 뉴스 서비스, 전역 예외처리, 회원 서비스, 카테고리 서비스, 키워드 서비스, 공지 서비스를 개발했고 Spring Cloud Gateway 적용을 했다.

 이 글에서는 어떤걸 만들었는지 간단한 소개와 회고 정도만 적어보려한다.

 

2. 이런건 주제 선정이 제일 어려워

기왕 하는거 재밌는걸 만들고 싶은 욕구가 컸다. 그 당시 배운 진도만으로 뭔가를 개발하기에는, 또한 뉴스 서비스에 대한 마땅한 아이디어가 떠오르지 않아 고생이었다. 아이디어가 생겨도, 크롬 익스텐션쪽이거나 이미 서비스 중인게 태반이었다.

 그러다가 예전에 어렸을 적에 생일을 입력하면 태어난 생년월일에 빌보드 1위를 했던 곡을 찾아주던 서비스가 생각났고, 접속한 날짜에 해당하는 과거의 핫했던 뉴스들을 AI를 통해 리서치해서 제공하는 서비스 아이디어가 생각나서 채택되어 프로젝트를 진행했다.

 

3. ERD, 시스템 아키텍처, 백엔드 디렉토리 구조

 

주말 포함 5일 남짓의 짧은 개발 기간을 받아 작은 규모의 서비스 개발을 기획했고, 팀원 중 미친 인프라 고수의 개인 홈서버를 활용하여 배포하기로 했다. 훗날 이 팀원은 최종 프로젝트까지 매번 나와 같은 팀을 하게 된다. (최종 때는 내가 꼬셔서 데려갔다. 덕분에 부족한 네트워크, 인프라 관련 지식들을 엄청 배우고 있다.)

 모놀리식으로 개발해도 됐지만, MSA 경험하러 이 캠프에 참여한게 컸기도 했고, 모놀리식은 너무 금방 끝날 것 같아서 불필요했지만 확장성을 고려했다고 생각하고 모놀리식 기반 + Spring Cloud Gateway, Eureka의 유사 MSA 패턴으로 개발하게 되었다.

 

아래는 백엔드 서비스의 디렉토리 구조이다.

.
├── build.gradle
├── gradle
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── src
    ├── main
    │   ├── java
    │   │   └── com
    │   │       └── lgcns
    │   │           └── newspacebackend
    │   │               ├── domain
    │   │               │   ├── news
    │   │               │   │   ├── controller
    │   │               │   │   │   ├── NewsAIController.java
    │   │               │   │   │   ├── NewsCategoryController.java
    │   │               │   │   │   └── NewsKeywordController.java
    │   │               │   │   ├── dto
    │   │               │   │   │   ├── NewsCategoryRequestDto.java
    │   │               │   │   │   ├── NewsCategoryResponseDto.java
    │   │               │   │   │   ├── NewsKeywordRequestDto.java
    │   │               │   │   │   ├── NewsKeywordResponseDto.java
    │   │               │   │   │   ├── NewsRequestDto.java
    │   │               │   │   │   └── NewsResponseDto.java
    │   │               │   │   ├── entity
    │   │               │   │   │   ├── NewsCategory.java
    │   │               │   │   │   └── NewsKeyword.java
    │   │               │   │   ├── repository
    │   │               │   │   │   ├── NewsCategoryRepository.java
    │   │               │   │   │   └── NewsKeywordRepository.java
    │   │               │   │   └── service
    │   │               │   │       ├── NewsAIService.java
    │   │               │   │       ├── NewsCategoryService.java
    │   │               │   │       └── NewsKeywordService.java
    │   │               │   ├── notice
    │   │               │   │   ├── controller
    │   │               │   │   │   └── NoticeController.java
    │   │               │   │   ├── dto
    │   │               │   │   │   ├── NoticeRequestDto.java
    │   │               │   │   │   └── NoticeResponseDto.java
    │   │               │   │   ├── entity
    │   │               │   │   │   └── Notice.java
    │   │               │   │   ├── repository
    │   │               │   │   │   └── NoticeRepository.java
    │   │               │   │   └── service
    │   │               │   │       └── NoticeService.java
    │   │               │   └── user
    │   │               │       ├── controller
    │   │               │       │   └── UserController.java
    │   │               │       ├── dto
    │   │               │       │   ├── LoginRequestDto.java
    │   │               │       │   ├── SignupRequestDto.java
    │   │               │       │   ├── UserInfoRequestDto.java
    │   │               │       │   └── UserInfoResponseDto.java
    │   │               │       ├── entity
    │   │               │       │   ├── User.java
    │   │               │       │   └── UserRole.java
    │   │               │       ├── repository
    │   │               │       │   └── UserRepository.java
    │   │               │       └── service
    │   │               │           └── UserService.java
    │   │               ├── global
    │   │               │   ├── config
    │   │               │   │   ├── ChatClientConfig.java
    │   │               │   │   ├── PasswordConfig.java
    │   │               │   │   └── RestTemplateConfig.java
    │   │               │   ├── entity
    │   │               │   │   └── TimeStamp.java
    │   │               │   ├── security
    │   │               │   │   ├── config
    │   │               │   │   │   ├── CorsConfig.java
    │   │               │   │   │   ├── SecurityConfig.java
    │   │               │   │   │   └── WebConfig.java
    │   │               │   │   ├── constant
    │   │               │   │   │   ├── GrantType.java
    │   │               │   │   │   └── TokenType.java
    │   │               │   │   ├── dto
    │   │               │   │   │   └── JwtTokenInfo.java
    │   │               │   │   ├── filter
    │   │               │   │   │   ├── JwtAuthenticationFilter.java
    │   │               │   │   │   └── JwtAuthorizationFilter.java
    │   │               │   │   ├── jwt
    │   │               │   │   │   ├── JwtTokenUtil.java
    │   │               │   │   │   └── JwtTokenUtilPractice.java
    │   │               │   │   ├── UserDetailsImpl.java
    │   │               │   │   ├── UserDetailsServiceImpl.java
    │   │               │   │   └── util
    │   │               │   │       └── FilterResponseUtil.java
    │   │               │   └── util
    │   │               │       └── FileUtil.java
    │   │               └── NewspaceBackendApplication.java
    │   └── resources
    │       └── static
    │           └── profile.png
    └── test
        └── java
            └── com
                └── lgcns
                    └── newspacebackend
                        └── NewspaceBackendApplicationTests.java

 

코드 컨벤션이나 디자인 패턴은 spring 개발 경험이 다른 팀원들이 별로 없어서 내 코드 스타일로 컨벤션이 채택되었다.

4. 주요 서비스 페이지

4.1 뉴스 메인 페이지

4.2 뉴스 목록 페이지

 

스크린샷 당시의 날짜는 2월 26일이었고, 날짜에 맞춰 과거의 이목을 끌었던 뉴스기사들을 AI가 리서치해서 응답해준 모습이다.

AI 모델은 OpenAI는 비용이 있어 사용하지 못했고, 무료 AI API를 제공해주는 GroqAI의 DeepSeek 모델을 사용했다. 무료 스펙이므로 성능이 좋지 않아 리턴값은 만족스럽지 못했지만, 최대한 구현을 목표로 개발했다.

 

5. 회고

LG CNS AM Inspire Camp 1기에서의 제대로된 첫 협업 프로젝트였다. 제대로됐다고 하기에는 기간이 짧아 아쉬운 점은 있었지만, 나와 비교적 어린 개발자분들과 함께 협업을 하며 신선함과 재미를 챙길 수 있었다. 개인적으로는 관심은 많았지만 매번 우선도에 밀려 사용해보지 못했던 Spring AI를 경험해본 것 또한 큰 의미가 있었다.

 짧은 기간이었지만 팀장으로서 믿고 따라와 준 팀원들에게, 그리고 매일 새벽까지 고생하며 함께 개발에 임해준 모습에 깊은 감사함을 느낀 프로젝트였다.

 또한, 우리 1기에 참여한 다른 수강생들의 실력이 기대 이상으로 뛰어나서(낮게 예상한 거 절대 아님.. 순위발표는 없었지만 우리가 반농담으로 1등인줄알았음..) 단 4~5일 만에 개발한 결과물이라고 믿기 어려울 정도의 퀄리티를 보고 큰 자극을 받았다. 슬슬 긴장이 풀릴 때쯤 다시 마음을 다잡게 해준, 의미 있는 첫 번째 미니 프로젝트였다.

 

꾸준할 것, 겸손할 것..

 

 

https://github.com/95hyun/newspace-backend?tab=readme-ov-file

 

GitHub - 95hyun/newspace-backend: LGCNS AM Inspire camp | 뉴스페이스 백엔드

LGCNS AM Inspire camp | 뉴스페이스 백엔드. Contribute to 95hyun/newspace-backend development by creating an account on GitHub.

github.com