전체 글 57

EC2에서 Multi-Version API 서버 구현하기

들어가며Laiteu의 포트폴리오 서비스를 운영하면서 큰 구조 변경이 필요한 시점이 왔습니다. V1 API는 MVP 단계에서 빠르게 구현되어 기본적인 기능을 제공했지만, 서비스가 성장하면서 새로운 요구사항들이 늘어났습니다. 특히 기존 API 구조로는 수용하기 어려운 데이터 모델의 근본적인 변경이 필요했고, API 응답 포맷도 전면적인 수정이 불가피했습니다.이러한 변경사항을 적용하면서도 이미 서비스 중인 웹 서비스와의 호환성을 유지해야 하는 과제가 있었습니다. 이에 따라 API 버전을 분리하는 전략을 선택했고, EC2 인스턴스 내에서 여러 버전의 API 서버를 동시에 운영하는 구조를 구현하게 되었습니다. 이 글에서는 다중 버전 API 서버 구현 과정에서 겪은 기술적 도전과 해결 방법을 공유하고자 합니다.실제..

작품 대표 이미지 저장

들어가며작품 포트폴리오 서비스를 개발하면서, 대표 이미지 지정 기능이 새롭게 추가되었습니다. 기존에는 업로드된 첫 번째 이미지를 대표 이미지로 사용했는데, 이는 MVP(Minimum Viable Product) 단계에서 충분했습니다. 하지만 알파테스터들의 피드백을 통해 대표 이미지를 직접 선택하고 싶다는 요구사항이 생겼고 이에 따라 저장 로직을 개선하게 되었습니다.문제 인식초기 개발 시에는 단순히 이미지 배열의 첫 번째 요소를 대표 이미지로 사용했습니다. 이는 다음과 같은 이유 때문이었습니다:단순한 구현: 별도의 필드나 로직 없이도 대표 이미지를 결정할 수 있었습니다.데이터 중복 최소화: 이미지 ID를 한 번만 저장하면 되었습니다.조회 성능: 대표 이미지를 가져올 때 추가적인 필드 조회가 필요 없었습니다..

API 응답 형식 표준화: 즐겨찾기 서비스 개선

들어가며API 응답 형식의 표준화는 백엔드 개발에서 매우 중요한 부분입니다. 일관된 응답 형식은 API를 사용하는 클라이언트 개발자의 개발 경험을 크게 향상시키고, 프로젝트의 유지보수성을 높여줍니다. 하지만 개발 초기 단계에서는 이러한 표준화의 중요성을 간과하기 쉽습니다. 빠른 기능 구현에 집중하다 보면, 각 API 엔드포인트마다 서로 다른 응답 형식을 가지게 되고, 이는 결국 기술 부채로 남게 됩니다.저희 팀도 작품 즐겨찾기 서비스를 개발하면서 이러한 문제를 경험했습니다. 프론트엔드 개발자가 API 연동 시 매번 다른 방식으로 응답을 처리해야 했고, 에러 처리도 일관성이 없었습니다. 이에 따라 API 응답 형식을 표준화하는 작업을 진행하게 되었고, 그 과정에서 많은 것을 배울 수 있었습니다.문제 인식처..

AWS 개발 환경 비용 최적화하기

들어가며AWS로 개발/운영 환경을 구축하고 있던 중, AWS Billing 대시보드를 검토하다가 예상보다 많은 비용이 발생하고 있는 것을 발견했습니다. Cost Explorer로 지난 3개월간의 비용 추이를 분석해보니 매월 약 15% 정도 비용이 증가하고 있었고, EC2 인스턴스와 관련 리소스들이 전체 비용의 80% 이상을 차지하고 있었습니다.문제 상황1. 높은 EC2 비용현재 prod 서버와 dev 서버를 모두 운영 중이었습니다. prod 서버는 m6g.medium($33.84), dev 서버는 t4g.medium($29.95) 인스턴스를 사용하고 있었는데, 월 EC2 비용만 $63.79가 발생하고 있었습니다.처음에는 dev 서버의 인스턴스 타입을 t4g.small로 다운그레이드하는 것을 고려했습니다...

[Spring] LAITEU 개발기 (2) - MongoDB를 활용한 데이터 모델링

들어가며지난 글에서 웹콘텐츠 창작자들을 위한 포트폴리오 서비스 LAITEU의 기획 배경과 전체적인 기술 스택에 대해 소개했습니다. 이번에는 "왜 MongoDB를 선택했는지", 그리고 "실제로 어떻게 데이터를 설계했는지"에 대해 이야기해보려 합니다.MongoDB, 무엇이 다른가?MongoDB를 설명하기 전에, 기존의 관계형 데이터베이스(RDBMS)와의 차이점부터 간단히 짚어보겠습니다.예를 들어 웹툰 작가의 작품을 저장한다고 생각해봅시다. 관계형 데이터베이스에서는 다음과 같은 테이블 구조가 필요할 것입니다:작품 테이블 (제목, 설명 등)작품 이미지 테이블 (이미지 URL, 순서 등)작품 태그 테이블 (태그 정보)작업 과정 테이블 (스케치, 선화, 채색 등의 단계별 정보)그런데 일러스트 작가의 경우는 어떨까요..

[Spring] LAITEU 개발기 (1) - 백엔드 아키텍처 설계

들어가며국내 웹콘텐츠 시장은 코로나19 이후 급격한 성장을 이루었고, 이에 따라 웹툰/웹소설/일러스트 작가부터 성우, 애니메이터, 역/식자, 편집자 등 콘텐츠 창작에 관여하는 창작자들의 수도 크게 증가했습니다. 하지만 시장의 성장 속도에 비해 체계적인 작업 과정과 분업화된 직무에 맞는 인력을 찾고 소통할 수 있는 플랫폼은 부재한 상황이었습니다.특히 창작자들의 소통은 주로 트위터나 카페 등에서 개별적으로 이루어지고 있었고, 작품이나 작업 과정을 아카이빙하기 위해서는 여러 플랫폼을 동시에 이용해야 하는 불편함이 존재했습니다. 게다가 구인구직을 위한 포트폴리오 제출 시에도 기존의 일반적인 포트폴리오 플랫폼들은 웹콘텐츠 창작자들의 특성을 제대로 반영하지 못했습니다.라이트(LAITEU)는 이러한 문제를 해결하기 ..

[JAVA] 문자열 압축, StringBuilder

오늘의 학습 키워드문자열 처리압축을 통한 변환Java의 문자열 메서드 사용오늘은 문자열을 특정 단위로 잘라서 반복되는 문자열을 압축하는 문제를 해결했습니다. 이를 위해 문자열을 다양한 단위로 잘라보고, 반복되는 문자열을 카운트하여 압축된 문자열의 길이를 구하는 방법을 배웠습니다. Java의 substring 메서드와 StringBuilder를 활용해 문자열을 처리했습니다.오늘의 회고어떤 문제가 있었고, 나는 어떤 시도를 했는지문자열을 특정 단위로 잘라서 반복되는 부분을 압축하여 가장 짧은 문자열을 찾는 문제였습니다. 처음에는 문자열을 단위로 자르는 방법과 반복되는 부분을 효율적으로 찾아내는 로직을 구현하는 데 어려움을 겪었습니다. class Solution { public int solution(S..

[알고리즘] 2024.07.25

[JAVA] 문자열 처리, HashMap

오늘의 학습 키워드문자열 처리맵핑을 통한 변환Java의 문자열 메서드 사용공부한 내용 본인의 언어로 정리하기오늘은 문자열 내에 일부 숫자를 영단어로 바꾼 문자열을 원래 숫자로 되돌리는 문제를 해결했습니다. 이를 위해 영단어와 숫자의 맵을 만들어서 문자열 내의 영단어를 대응하는 숫자로 치환하는 방법을 배웠습니다. Java의 HashMap을 활용해 영단어와 숫자를 매핑하고, replace 메서드를 이용해 문자열을 변환했습니다.오늘의 회고어떤 문제가 있었고, 나는 어떤 시도를 했는지 문자열 내에 숫자와 영단어가 혼합되어 있는 경우 이를 정확히 숫자로 변환하는 문제였습니다. 처음에는 숫자가 아닌 문자를 숫자로 변환하기 위해 반복문과 조건문을 사용하려 했습니다. 숫자가 아닌 문자를 만나면 이를 변환해 숫자로 바꾸..

[알고리즘] 2024.07.25

[JAVA] 배열의 최대공약수 계산

오늘의 학습 키워드최대공약수 (GCD) 계산배열의 최대공약수조건을 만족하는 가장 큰 양의 정수 찾기유클리드 알고리즘공부한 내용 본인의 언어로 정리하기오늘의 문제 : 숫자 카드 나누기 https://school.programmers.co.kr/learn/courses/30/lessons/135807class Solution { public int solution(int[] arrayA, int[] arrayB) { // 1번 조건 - 배열의 최대 공약수 int gcdA = gcdOfArray(arrayA); int gcdB = gcdOfArray(arrayB); // 2번 조건 - 다른 배열을 하나도 나눌 수 없는지 확인 i..

[알고리즘] 2024.07.23

[Spring Security] 시큐리티 환경 설정과 로그인

백기선님의 스프링부트 개념과 활용 기본편 강의를 듣고, 정리한 글입니다. 스프링부트 시큐리티 환경을 설정하고 회원가입과 로그인까지 완료했습니다. 환경 설정 MySQL 세팅 먼저 MySQL Workbench에서 MySQL DB와 사용자를 생성한다. create user 'cos'@'%' identified by 'cos12345'; GRANT ALL PRIVILEGES ON *.* TO 'cos'@'%'; create database security; use security; ERROR 1819 (HY000): Your password does not satisfy the current policy requirements. 프로젝트 생성 인텔리제이의 Spring Initializer를 사용해 프로젝트를 생..

[Spring] 2023.01.18

[Spring Security] WebSecurityConfigurerAdapter Deprecated 해결

더이상 WebSecurityConfigurerAdapter를 지원하지 않는다. spring 공식문서에 가보니 Spring Security 5.7.0-M2 부터는 구성 요소 기반 보안 설정으로 변경된다는 이슈가 있었다. WebSecurityConfigurerAdapter 상속 후, configure 메소드를 오버라이딩 하여 설정하는 방식에서 SecurityFilterChain를 빈으로 등록하는 방식으로 변경했다고 한다. // 이전 방식 // WebSecurityConfigurerAdapter 상속 후, configure 메소드 오버라이딩 @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdap..

[Spring] 2023.01.18

[Spring] 템플릿 엔진 Mustache의 장점

Template Engine 템플릿 엔진은 지정된 템플릿 양식과 데이터를 합쳐 HTML 문서를 출력하는 소프트웨어이다. 웹사이트 화면을 어떤 형태로 만들지 도와주는 양식이라고 할 수 있다. 템플릿 엔진을 사용하면 하나의 템플릿으로 여러 페이지를 렌더링할 수 있기 때문에 재사용성이 높고 유지보수가 용이하다. 템플릿 엔진은 서버 사이드 템플릿 엔진과 클라이언트 사이드 템플릿 엔진으로 나눌 수 있다. Server Side Template Engine 서버 사이드 템플릿 엔진은 서버에서 DB 혹은 API에서 가져온 데이터를 미리 정의된 템플릿에 넣어 HTML을 그린 뒤 클라이언트에 전달해주는 역할을 한다. HTMl 코드에서 고정적으로 사용되는 부분은 템플릿으로 만들어두고 동적으로 생성되는 부분만 템플릿 특정 장..

[Spring] 2023.01.17

[Java] Builder 패턴

Java에서 객체를 생성할 때 사용하는 패턴은 대표적으로 생성자 패턴과 빌더 패턴이 있다. 빌더 패턴을 사용하면 지금 채워야 할 필드가 무엇인지 명확하게 지정할 수 있다. 예시부터 보면.. public class Person { private String name; private String grade; public Person(String name, String grade){ this.name = name; this.grade = grade; } // Getter public String getName() { return name; } public String getGrade() { return grade; } } String name = "jueun"; String grade = "4"; Person..

[Spring] 2023.01.16

[Spring] ERROR 1819 (HY000): Your password does not satisfy the current policy requirements.

비밀번호 정책에 맞지 않는 단순한 비밀번호를 설정해서 생긴 오류이다. '12345'처럼 단순한 비밀번호를 사용하려면 validate_password_policy를 낮춰야 한다. 현재 validate_password_policy는 "SHOW VARIABLES LIKE 'validate_password%';"로 확인할 수 있다. validate_password_policy는 LOW, MEDIMU, STRONG 3단계로 구성되어 있다. validate_password_policy를 최소 8자 이상 Length만 지켜주면 되는 LOW로 낮춰준다. mysql> set global validate_password.policy=LOW; Query OK, 0 rows affected (0.00 sec) 다시 user를..

[Spring] 2023.01.15

[기록] 2022년 회고 - 컴공 2학년

2022년은 열심히 놀고 열심히 공부한 해라고 할 수 있다. 3개의 동아리, 1개의 커뮤니티에 들어가 다양한 사람들을 만났고, 4번의 MT, 6번의 여행을 가며 코로나 기간 놀지 못한 한을 풀었다. 또 6개의 스터디를 하며 지식을 쌓았고, 5개의 프로젝트를 하며 개발자가 되기 위한 준비를 했다. 당연히 양보단 질이 중요하지만 이정도 양이면 스스로에게 열심히 살았다고 말해줄 수 있을 것 같다. 2022년에 생긴 모든 에피소드를 적다 보면 글이 끝나지 않을 것 같아 대학 생활 위주로 적어보려고 한다. 1월 1월에는 ‘출석하는 동안’ 프로젝트를 했다. 학생 정보를 관리하고 출석을 확인할 수 있는 웹사이트를 개발하는 프로젝트였는데, 백엔드 응애 시절이라 DB 파트로만 참여했다. 이때 처음으로 SQL문을 공부하면..

[기록] 2023.01.03

[기록] 2022 요약

프로젝트 학생 출석 관리 웹사이트 ‘출석하는 동안, 22/01 - 22/03 홍대생 맛집 지도 서비스 ‘멋사먹자’ 22/07 - 22/09 태그별 카페 추천 앱, ‘홍카추’ 프로젝트 관리 캘린더 웹, '도치의 마지막 잎새' 동아리 & 스터디 개발 연합동아리 멋쟁이사자처럼 대학 10기 백엔드 파트 멤버 22/03 - 22/09 신촌연합해커톤 ‘trash-off’ 22/08/27 개발 학회 Google Developer Student Clubs Hongik Univ. 1st Core Member 22/07 - 23/09 백엔드 파트 멤버 22/03 - 22/09 개발 커뮤니티 BlazingDevs Django 스터디 ‘장고열명타요’ 22/03 - 22/06 알고리즘 자기주도학습동아리 ‘야나두리즘’ 22/03..

[기록] 2023.01.01

[C++ 자료구조론] 3장 스택과 큐

템플릿 template 클래스와 함수의 재사용에 기여 템플릿/ 인수화 타입을 이용해 소프트웨어 개발 시간과 비용 절약 SelectionSort 선택 정렬 기법을 이용해 n ≥1 개의 서로 다른 T의 집합을 정렬 사용 시 주의사항 사용자 정의 데이타 타입에 대해서는 별도로 정의해야 한다. template // 이 부분을 반드시 넣어야 한다. void SelectionSort(T *a, const int n) { // n개의 T a[0]부터 a[n-1]까지 비감소 순으로 정렬한다. for ( int i = 0; i < n; i++) { int j = i; // a[i]와 a[n-1] 사이에 가장 작은 T를 찾는다 for (int k = i + 1; k < n; k++) if (a[k] < a[j]) j = ..

[C++ 자료구조론] 2장 배열

추상 데이타 타입과 C++ 클래스 명세와 구현을 구별하고 사용자로 부터 ADT의구현을 은닉하기 위해 클래스(class) 제공 클래스 구성 클래스 이름 데이터 멤버 (멤버 변수) 멤버 함수 💡 프로그램 접근 레벨 : public, private, protected public : 프로그램 어디에서도 접근 private : 같은 클래스 내, friend로 선언된 함수나 클래스에 의해 접근 protected : 같은 클래스 내, 서브클래스, friend에 의해서만 접근 데이터 추상화와 캡슐화 데이터 멤버 모두 private 또는 protected로 선언해 캡슐화 멤버 함수 외부에서 접근할 것은 public으로 선언하고 나머지는 private이나 protected로 선언 멤버 함수의 구현은 inline 함수로 ..

[C++ 자료구조론] 1장 기본 개념

대형 컴퓨터 시스템 💡 설계 및 구현 방법론 데이터 추상화 알고리즘 명세 성능 분석과 측정 대형 컴퓨터 프로그램 복잡하게 상호작용하는 부품들로 구성된 시스템 like 하드웨어 시스템 생명 주기가 있음 why? 유지보수를 위해 시스템 생명 주기 System Life Cycle 💡 요구사항 - 분석 - 설계 - 정제와 코딩 - 검증 (과정을 하나라도 생략하면 안 된다.) 요구사항 requirement 프로젝트들의 목적을 정의한 명세들의 집합 고객의 요구에 맞게 입력과 출력에 관한 정보를 기술 분석 analysis 문제들을 다룰 수 있는 작은 단위들로 나눔 = 분할 정복 Divide and Conquer 상향식 bottom-up VS 하향식 top-down 상향식은 C의 함수처럼 기능 위주로 나눈다. 버그 난..

[DB] 기본키가 두 개일 수 있나?

사용자가 추천하는 식당을 저장하는 테이블을 설계하면서 기본키를 설정할 때 문제가 생겼다. 테이블은 행을 구분할 수 있는 기본키가 필요하다. ‘사용자 - 식당’ 테이블에서 행을 구분하려면 사용자 ID와 식당 ID가 동시에 사용되어야 한다. 이때 기본키는 두 개가 될 수 있나?라는 의문이 들었다. 정답은 아니다. 기본키는 하나인 게 맞고 기본키를 구성하는 열이 두 개인 것이다. 기본키가 하나라는 말이 기본키를 구성하는 열이 하나여야 한다는 뜻은 아니다. ‘사용자 - 식당’ 테이블처럼 하나의 열만으로는 행을 특정하기 어려울 수 있다. 이때는 복합키를 만들어 기본키로 사용하면 된다. 참고 https://hoon93.tistory.com/57

[DB] 2022.07.07

[DB] UUID를 Primary Key로 사용하기

UUID UUID는 범용 고유 식별자로 네트워크 상에서 서로 다른 개체들을 구별하기 위해 만들어진 방법이다. 32개 문자, 4개의 하이픈으로 이루어져 있어 생성할 수 있는 값은340,282,366,920,938,463,463,374,607,431,768,211,456개이다. 고유성을 완벽하게 보장할 수는 없지만, 실제 사용할 때 중복될 가능성이 거의 없다 UUID는 생성 방법에 따라 5가지 vertion이 존재한다. TimeStamp 기반의 v1/v2, 완전 Random 기반의 v4, Hashing 기반의 v3/v5가 존재한다. 실제로는 v1/v4/v5가 주로 이용된다. Random한 UUID를 생성하기 위해서는 v1/v4를 이용하고, 고정된 UUID를 생성하기 위해서는 v5를 이용한다. PK를 생성할 ..

[DB] 2022.07.07

[DB] 데이터 모델링은 어떻게 하는걸까?

먼저 데이터 모델이란? 데이터 모델은 현실의 정보를 컴퓨터로 표현한 개념적 모형이다. 데이터베이스 설계 과정에서 데이터의 구조(Schema)를 논리적으로 묘사하기 위해 사용한다. 그럼 데이터 모델링은? DB의 골격을 이해하고 SQL문을 효율적으로 작성하기 위해 필요한 과정이다. 정보시스템 구축의 대상이 되는 업무 내용을 분석하고 약속된 표기법에 의해 표현하는 걸 의미한다. 분석된 모델은 실제 데이터베이스 생성하고 개발 및 데이터 관리 시 사용된다. 데이터 모델링 절차 데이터 모델링은 업무 파악 → 개념적 데이터 모델링 → 논리적 데이터 모델링 → 물리적 데이터 모델링 순으로 이루어진다. 업무 파악 주어진 업무를 정확하게 파악하는 단계이다. 어떤 기능들이 필요한 지 개념을 잡는다. 개념적 데이터 모델링 현..

[DB] 2022.07.07

[Django] Template 상속

Template 상속이란 템플릿 상속은 중복되는 코드를 한 번만 작성하고 재사용하는 기능이다. 중복되는 코드를 하나의 html 파일에 넣어 효율적으로 관리한다. {% extends 'base.html' %} {% block content %} {% endblock %} Template 상속 방법 1. 중복되는 코드를 넣어놓을 파일 하나를 생성한다. 주로 파일 이름은 base.html로 생성한다. (app/templates/base.html 생성) 2. 중복되는 코드를 모두 복사 붙여넣기하여 넣는다. 3. 중복 코드 중간에 페이지별로 다른 코드가 들어가는 부분에는 {% block content %}와 {% endblock %}을 넣는다. {% block content %}, {% block preview ..

[장고] 2022.06.30

[Django] Static 파일 관리하기

Static 파일이란 정적 파일은 HTML을 제외하고 웹 페이지를 렌더링할 때 필요한 추가 파일들이다. 웹 서버에 미리 저장되어 있어 css, image 파일 같이 컨텐츠가 고정되어있고 사용자의 요청에 따라 변하지 않는다. 정적 파일에는 static파일과 media파일이 있는데, static 파일은 개발자가 미리 준비한 데이터이고, media 파일은 사용자가 업로드한 데이터이다. Static 파일 생성 app 디렉토리 안에 static 디렉토리를 생성한다. css, js, img 등 필요한 static 파일들을 저장한다. Static 경로 설정 어디에 static 파일이 저장되어 있는지 장고에게 위치를 알려주어야한다. STATIC_URL을 '/static/'로 설정해 ‘/static/’ 경로로 요청이 들..

[장고] 2022.06.30

[Django] URL 매핑 - include를 이용한 url 계층 관리

url을 연결할 때 urlpatterns에 path들을 단순 나열해두면 path가 많아졌을 때 관리가 어렵다. # myproject/urls.py from django.contrib import admin from django.urls import path # import 해주기 import myapp.views urlpatterns = [ # 일 때 path('admin/', admin.site.urls), # 일 때 myapp.views.home함수를 실행해라 path('', myapp.views.home, name='hello_world'), ] 그래서 django.urls의 include를 사용해 url들을 계층적으로 관리한다. 먼저 url을 관리할 앱을 myproject/setting.py의 I..

[장고] 2022.06.30