본문 바로가기

분류 전체보기

(136)
[CSS] 반응형 웹디자인 (미디어 쿼리, 컨테이너 쿼리) CSS Media Queries - 모바일 퍼스트 철학과 반응형 웹(Responsive Web)의 심장2010년 이전의 웹 개발자들에게 세상은 참으로 평온했습니다. 사용자들이 웹사이트에 접속하는 기기라고는 오직 책상 위에 놓인 커다란 1024x768 또는 1280x1024 해상도의 PC 모니터밖에 없었기 때문입니다. 개발자들은 화면을 가로 980px 픽셀로 고정(Fixed)시켜 놓고, 그 안에 로고와 메뉴, 본문을 예쁘게 배치하기만 하면 일이 끝났습니다. 그러나 스티브 잡스가 아이폰(iPhone)을 세상에 내놓으며 모바일 혁명이 시작되자, 평화롭던 프론트엔드 세계에 헬게이트가 열렸습니다. PC용으로 만든 980px짜리 거대한 웹사이트를 320px짜리 조그만 스마트폰 화면으로 욱여넣으려니, 글씨는 깨알만 ..
[CSS] Grid Layout - 2차원 레이아웃 마스터 클래스 CSS Grid (1편: 기초) - Flexbox의 한계를 박살 내는 2차원 바둑판 레이아웃의 탄생이전 포스팅에서 우리는 레이아웃의 구원자 Flexbox에 대해 깊이 열광했습니다. Flexbox는 가로 한 줄(Row) 또는 세로 한 줄(Column)의 요소를 우아하게 정렬하는 데 있어 타의 추종을 불허하는 신적인 존재입니다. 하지만 완벽해 보이는 Flexbox에게도 뼈아픈 한계가 하나 존재합니다. 바로 '1차원(1-Dimensional) 선형 시스템'이라는 족쇄입니다.갤러리 이미지들이나 복잡한 대시보드 화면처럼 가로와 세로가 동시에 정교하게 딱딱 맞아떨어지는 '바둑판(2차원 면)' 형태의 격자 레이아웃을 짜려고 하면, Flexbox는 갑자기 바보가 됩니다. 가로 한 줄을 맞추고 flex-wrap으로 아래..
[CSS] Flexbox: 1차원 레이아웃의 모든 것 과거 웹 개발자들은 화면에 박스 3개를 나란히 가로로 배치하기 위해 피눈물을 흘렸습니다. float: left를 썼다가 부모의 높이가 찌그러지는 버그(Height Collapse)를 막기 위해 clearfix라는 흑마법 코드를 덧붙여야 했고, 수직 중앙 정렬(Vertical Center)을 맞추기 위해 line-height와 margin 값을 픽셀 단위로 찍어 맞추는 노가다를 반복했습니다. 브라우저 창 크기가 변하는 스마트폰 시대가 도래하자, 이 구시대적 해킹 레이아웃은 여지없이 와르르 무너져 내렸습니다.전 세계 개발자들의 절규를 들은 W3C는 마침내 2009년 초안을 거쳐, 오로지 '레이아웃 배치'만을 목적으로 창조된 완벽하고 우아한 구세주를 세상에 내놓았습니다. 그것이 바로 CSS Flexible B..
[CSS] 가상 클래스(Pseudo-classes)와 가상 요소 활용 프론트엔드 개발에 갓 입문한 주니어들은 웹사이트에 역동성(Interaction)을 부여하기 위해 조금만 조건이 복잡해져도 득달같이 자바스크립트(JavaScript)를 꺼내 듭니다. "마우스를 올리면 버튼 색을 파랗게 바꿔야 하니까 mouseover 이벤트를 달아야지!", "리스트 항목 중 홀수 번째 줄에만 회색 배경을 칠해야 하니까 for 반복문으로 인덱스를 체크해서 클래스를 add 해야지!" 하지만 돔(DOM) 트리를 자바스크립트로 직접 건드리는 렌더링 성능(Performance)을 갉아먹는 주범입니다.CSS의 세계에는 이미 HTML 문서의 껍데기를 찢지 않고도 요소의 '특정 상태(마우스 오버, 포커스 등)'나 '구조적 순서(첫 번째, n번째 자식 등)'를 귀신같이 추적해 내는 첩보원 같은 존재들이 내..
[CSS] 레이아웃 기초 마스터 (Position, Float, Z-index) 우리가 이전 포스팅에서 배웠던 Block과 Inline의 법칙은 브라우저의 아주 정직한 '일반 흐름(Normal Flow)'을 따릅니다. 위에서부터 아래로, 왼쪽에서 오른쪽으로 차곡차곡 박스를 쌓아가는 방식이죠. 하지만 현대의 웹 디자인은 결코 정직하지 않습니다. 스크롤을 한참 내려도 상단에 끈질기게 달라붙어 있는 네비게이션 헤더(Header), 프로필 사진의 오른쪽 구석에 살짝 걸쳐서 깜빡이는 빨간색 알림(Badge) 동그라미, 그리고 화면 정중앙에 떡하니 나타나서 뒤의 내용들을 다 가려버리는 모달(Modal) 팝업창까지!이 모든 화려한 UI들은 기존 요소들을 밀어내지 않고, 마치 포토샵의 '레이어(Layer)'처럼 공중에 붕 떠서 자신만의 3차원 Z축 공간을 확보합니다. 문서의 일반적인 흐름(중력)을..
[CSS] 웹 타이포그래피와 시각적 디자인 기초 웹사이트에 처음 접속한 사용자가 이 사이트가 트렌디한지, 신뢰할 만한지, 아니면 10년 전에 만들어진 낡은 사이트인지 판별하는 데 걸리는 시간은 0.05초(50 밀리초)에 불과하다고 합니다. 텍스트를 읽기도 전에 우리의 뇌는 화면의 '색상(Color)'과 '배경(Background)'이 뿜어내는 분위기를 본능적으로 스캔하여 결론을 내려버립니다. 프론트엔드 개발자가 레이아웃과 단위를 아무리 기가 막히게 짜놓아도, 버튼 색상이 촌스러운 순수 빨간색(red)이거나 배경 이미지가 찌그러져 있다면 그 프로젝트는 처참하게 실패한 것입니다. 웹에서의 색상은 단순한 미적 장식이 아닙니다. 클릭을 유도하는 심리적 넛지(Nudge)이자, 다크 모드를 전환하는 아키텍처의 핵심 데이터입니다.이번 글에서는 컴퓨터가 색상을 인식..
[CSS] CSS 기초와 핵심 개념 완벽 가이드 웹 개발에 입문하는 거의 모든 사람들이 가장 먼저 접하는 기술은 HTML과 CSS입니다. 코딩 부트캠프나 온라인 강의에서는 보통 "HTML은 웹페이지의 뼈대를 만들고, CSS는 그 뼈대를 예쁘게 꾸미는 역할을 합니다"라고 간단명료하게 설명하고 넘어갑니다. 하지만, 깊이 있는 프론트엔드 개발자가 되기 위해서는 단순히 이 문장을 외우는 것을 넘어, "대체 왜 처음부터 HTML 자체에 디자인 기능을 빵빵하게 넣지 않고 굳이 CSS라는 완전히 다른 언어를 발명하여 분리해야만 했는가?"에 대한 역사적이고 구조적인 근본 이유를 이해해야 합니다. 이 분리의 철학을 이해하지 못하면, 실무에서 수천 줄의 코드가 얽히고설키는 유지보수 지옥을 마주하게 됩니다.오늘 포스팅에서는 초창기 웹의 혼란스러웠던 역사부터 CSS가 가져..
[GIT] 오픈소스 기여(Fork, PR)와 대규모 다중 저장소(Submodule), 생산성 향상 팁 우리가 매일 사용하는 방대한 라이브러리들과 프레임워크들의 대다수는 전 세계 수많은 개발자들이 대가 없이 기여하는 '오픈소스(Open Source)' 형태로 운영됩니다. 그렇다면 내가 권한을 가지고 있지도 않은 다른 사람의 저장소에 어떻게 내 코드를 안전하게 기여할 수 있을까요? 혹은 사내에서 서로 다른 팀의 프로젝트를 수정해야 할 때는 어떤 방식을 사용해야 할까요? 이때 등장하는 핵심 개념이 바로 Fork(포크)와 Pull Request(풀 리퀘스트, PR)입니다.이번 포스팅에서는 GitHub를 중심으로, 남의 저장소를 복제해와서 코드를 수정하고 원작자에게 병합을 요청하는 아름다운 협업 프로세스에 대해 상세히 알아보겠습니다.1. 남의 저장소를 내 계정으로 복사하기: Fork오픈소스 프로젝트나 타 팀의 저..
[GIT] Git 디버깅(Blame, Bisect)과 실무 브랜치 전략(Git Flow) 잘 돌아가던 서비스에 갑자기 치명적인 버그가 발생했습니다. 수만 줄의 코드 중에서 어느 파일의 어느 줄 때문에 문제가 생겼는지, 그리고 도대체 언제, 누가 이 코드를 작성했는지 알아내야만 신속하게 장애를 해결할 수 있습니다. Git은 코드가 엉망이 되었을 때 탐정처럼 단서를 찾아주는 훌륭한 디버깅 전용 명령어들을 내장하고 있습니다.이번 포스팅에서는 특정 코드 라인의 작성자와 커밋을 찾아내는 git blame과, 방대한 커밋 히스토리 속에서 이진 탐색(Binary Search) 알고리즘을 활용해 버그가 처음 발생한 지점을 기가 막히게 찾아내는 git bisect의 활용법을 완벽히 마스터해 보겠습니다.1. 코드 라인별 책임자 추적: git blame특정 파일의 코드를 열어보았을 때, 도대체 이 기괴한 로직을..
[GIT] 커밋 정밀 조작(Amend, Cherry-pick)과 최후의 복구 도구(Reflog) 방금 커밋을 남겼는데 치명적인 오타를 발견했거나, 빠뜨린 파일이 뒤늦게 생각난 적이 있으신가요? 혹은 다른 팀원이 작업한 수십 개의 커밋 중 딱 하나의 커밋(특정 기능 추가)만 내 브랜치로 가져오고 싶은 경우가 발생할 수 있습니다. Git은 이처럼 세밀한 단위의 커밋 조작을 가능하게 하는 강력한 도구들을 제공합니다.이번 포스팅에서는 최신 커밋을 살짝 수정하여 완벽하게 다듬는 git commit --amend와, 다른 브랜치에서 내가 원하는 특정 커밋만 쏙쏙 골라 담는 git cherry-pick의 활용법에 대해 자세히 살펴보겠습니다.1. 방금 남긴 커밋 수정하기: git commit --amendgit commit --amend는 말 그대로 가장 마지막에 남긴(최신) 커밋을 '고치는' 명령어입니다. 단순..
[GIT] 깔끔한 히스토리를 위한 Rebase와 안전한 작업 되돌리기 협업을 진행하며 브랜치를 생성하고 git merge로 코드를 병합하다 보면, 수많은 병합 커밋(Merge Commit)이 생성되어 커밋 히스토리가 거미줄처럼 복잡해집니다. 프로젝트의 규모가 커질수록 누가 언제 어떤 목적으로 코드를 변경했는지 한눈에 파악하기가 매우 어려워집니다. 이러한 문제를 해결하고 마치 한 사람이 순차적으로 개발한 것처럼 깔끔한 일직선 히스토리를 만들어주는 강력한 도구가 바로 Git Rebase입니다.이번 포스팅에서는 Rebase의 작동 원리와 git merge와의 결정적인 차이점, 그리고 실무에서 Rebase를 사용할 때 반드시 지켜야 할 주의사항에 대해 자세히 알아보겠습니다.1. Rebase란 무엇인가?Rebase라는 단어는 'Base(기준점)를 Re(다시) 설정한다'는 의미를 가..
[GIT] Git 브랜치(Branch) 완벽 이해와 병합 충돌(Conflict) 해결 소프트웨어를 개발할 때는 메인 서비스를 유지하면서 동시에 새로운 기능을 추가하거나 버그를 수정하는 작업이 수시로 일어납니다. 만약 하나의 흐름(메인 코드베이스) 위에서 모든 개발자가 코드를 직접 수정한다면, 코드가 엉키고 완성되지 않은 기능이 서비스에 노출되는 등 끔찍한 문제가 발생할 것입니다. Git은 이러한 문제를 해결하기 위해 코드를 통째로 복사한 것처럼 독립적인 작업 환경을 제공하는 브랜치(Branch) 기능을 지원합니다.이번 포스팅에서는 브랜치의 핵심 개념부터 생성, 이동, 그리고 삭제까지 브랜치 라이프사이클 전반에 대해 상세히 알아보겠습니다.1. 브랜치(Branch)란 무엇인가?브랜치를 우리말로 번역하면 '나뭇가지'입니다. Git에서의 브랜치는 나무의 기둥(메인 코드)에서 뻗어 나온 나뭇가지처..
[GIT] 커밋 히스토리 관리 및 원격 저장소(GitHub) 협업 코드를 수정하고 커밋(Commit)하는 과정을 반복하다 보면, 프로젝트에는 수많은 버전의 스냅샷이 쌓이게 됩니다. 이렇게 누적된 기록을 효과적으로 열람하고 분석하는 것은 과거의 변경 사항을 추적하거나 특정 시점으로 되돌아가기 위해 매우 중요합니다. 또한, 빌드 결과물이나 개인적인 설정 파일 등 버전 관리에 포함되어서는 안 되는 파일들을 사전에 차단하는 설정도 필수적입니다.이번 포스팅에서는 git log를 활용한 히스토리 분석 기법과 .gitignore 파일을 통한 파일 추적 제외 방법에 대해 자세히 알아보겠습니다.1. Git 히스토리 조회: git log지금까지 프로젝트에서 남긴 모든 커밋 내역을 확인하려면 git log 명령어를 사용합니다. 이 명령어를 인자 없이 실행하면 가장 최근의 커밋부터 시간의 ..
[GIT] Git 시작하기와 로컬 워크플로우 완벽 가이드 현대 소프트웨어 개발 환경에서 버전 관리 시스템(Version Control System, VCS)은 선택이 아닌 필수입니다. 수많은 개발자가 협업하고, 방대한 양의 코드가 매일 변경되는 상황에서 안정적으로 소스 코드를 관리하는 것은 프로젝트의 성공과 직결됩니다. 이 중에서도 Git은 전 세계적으로 가장 널리 사용되는 분산형 버전 관리 시스템입니다.이 포스팅에서는 Git이 무엇인지 그 핵심 개념을 정확히 이해하고, 운영체제별 설치 방법과 초기 환경 설정까지 상세하게 살펴보겠습니다.1. 버전 관리 시스템과 Git의 필요성소프트웨어 프로젝트를 진행하다 보면 동일한 파일에 여러 번 수정을 가하게 됩니다. 단순히 파일명을 `project_v1`, `project_v2_final`, `project_v3_진짜최종..
gRPC 통신: JSON을 버리고 초고속 바이너리(Binary)를 달다 우리는 지금까지 프론트엔드와 백엔드가 데이터를 주고받을 때 당연하다는 듯이 { "name": "철수", "age": 25 } 같은 JSON (JavaScript Object Notation) 텍스트 포맷을 사용해 왔습니다. 사람이 읽기 쉽고 브라우저에서 잘 돌아가기 때문입니다. 그런데 마이크로서비스(MSA) 환경에서 뒷단의 [주문 서버]와 [결제 서버]끼리 통신할 때도 과연 사람이 읽기 쉬운 JSON을 써야 할까요? "name": 이라는 껍데기 글자를 파싱(Parsing)하느라 서버의 CPU와 네트워크 대역폭이 엄청나게 낭비됩니다. 구글(Google) 아저씨들은 참다못해 "서버끼리 통신할 때는 사람 눈치 보지 마! 텍스트(JSON) 집어치우고 컴퓨터가 제일 좋아하는 0과 1(이진수, Binary)로 압축..
GraphQL (그래프QL): REST API의 한계를 부수고 오버패칭을 끝내다 우리는 지난 십수 년간 REST API를 절대 진리라 믿고 사용해 왔습니다. 하지만 화면이 좁고 네트워크가 불안정한 모바일(Mobile) 시대가 오면서 REST API의 두 가지 치명적인 한계가 드러나기 시작했습니다. 첫째, 오버패칭(Over-fetching)입니다. 모바일 앱 메인 화면에 유저의 "닉네임" 딱 하나만 필요한데, 백엔드가 만들어둔 GET /users/1 API를 찌르면 이메일, 주소, 장바구니 내역까지 쓸데없는 수십 개의 데이터가 한꺼번에 쏟아집니다(데이터 낭비). 둘째, 언더패칭(Under-fetching)입니다. 유저의 프로필, 최근 본 상품, 찜한 목록을 하나의 화면에 그리려면 /users/1, /products/recent, /favorites 세 개의 API를 세 번이나 찔러야 ..
[Spring Boot] Spring HATEOAS: 진정한 REST API (Level 3) 달성기 웹 개발을 하다 보면 누구나 "우리는 RESTful API를 사용합니다"라고 말합니다. HTTP 메서드(GET, POST, PUT, DELETE)를 쓰고 /users/1처럼 예쁜 URL을 쓰면 REST API라고 믿기 때문입니다. 하지만 REST의 창시자인 '로이 필딩(Roy Fielding)' 아저씨는 분노하며 "네놈들이 만든 건 진짜 REST가 아니야!"라고 일갈합니다. 로이 필딩이 말하는 완벽한 REST(Richardson Maturity Model Level 3)의 필수 조건 중 하나는 바로 HATEOAS (Hypermedia As The Engine Of Application State)입니다. 이름이 끔찍하게 길지만 핵심은 단순합니다. "서버가 클라이언트(앱/웹)에게 응답(JSON)을 내려줄 ..
[Spring Boot] 낙관적 락(Optimistic) vs 비관적 락(Pessimistic): JPA 동시성 제어 이전 40단계에서는 거대한 트래픽이 몰려올 때 Redis 인프라를 활용하여 재고 초과 판매(동시성) 문제를 막아내는 '분산 락'을 배웠습니다. 하지만 Redis는 구축하고 유지보수하기 까다로운 외부 인프라입니다. 만약 "우리 회사는 그 정도로 트래픽이 몰리지 않는데 굳이 Redis까지 깔아야 하나요? 그냥 JPA랑 MySQL 기능만으로 동시성을 막을 수는 없나요?"라고 묻는다면 정답은 "가능하다!"입니다. 실무에서는 동시성 문제가 터질 확률(충돌 빈도)에 따라, 애플리케이션 레벨에서 가볍게 막아내는 낙관적 락(Optimistic Lock)과 DB 레벨에서 아예 진짜 자물쇠를 걸어버리는 비관적 락(Pessimistic Lock)을 골라서 사용합니다.이번 [Spring Boot 실무 완벽 가이드] 시리즈에서..
[Spring Boot] Redis 분산 락(Distributed Lock): 분산 환경의 동시성 제어 여러분이 티켓팅 사이트를 만들었고, 아이유 콘서트 VVIP석이 딱 1자리 남았습니다. 사용자 A와 B가 0.001초의 오차도 없이 동시에 [예매하기] 버튼을 눌렀습니다. 두 요청은 백엔드의 checkInventory()(재고가 1개 이상인지 확인)를 동시에 통과하고, decreaseInventory()(재고 차감)를 동시에 실행해버립니다. 결과는 재고는 1개인데 2명에게 예매가 완료되어 버리는 '초과 판매(Over-selling)' 대참사입니다. 학원 프로젝트(단일 서버)에서는 메서드에 synchronized 키워드를 붙여서 쓰레드를 줄 세우면 해결되었습니다. 하지만 우리의 실무 환경은 스프링 부트 서버가 10대 떠 있는 분산 환경(Scale-Out)입니다. 1번 서버의 JVM synchronized가 ..
[Spring Boot] Resilience4j 서킷 브레이커: 장애의 도미노를 끊어라 마이크로서비스 아키텍처(MSA)는 시스템을 여러 개로 쪼개어 하나가 죽어도 다른 놈들은 살려두자는 철학에서 출발했습니다. 그런데 현실은 정반대입니다. 사용자가 상품을 주문할 때, [주문 서버] -> [결제 서버] -> [재고 서버] 순으로 서로 꼬리를 물고 API를 호출(RestTemplate, FeignClient)하는 구조를 생각해 봅시다. 만약 막내인 [재고 서버]의 DB에 락(Lock)이 걸려 응답이 10초씩 지연된다면? 결제 서버도 응답을 기다리다 쓰레드(Thread)가 고갈되어 멈추고, 주문 서버도 멈추고, 결국 맨 앞의 클라이언트(앱)도 하얀 화면에서 멈춰버립니다. 제일 밑바닥에 있는 마이크로서비스 하나가 아프면, 그를 호출하는 모든 윗단 서비스들이 도미노처럼 쓰러져(Cascading Fai..
[Spring Boot] Eureka 서비스 디스커버리 마이크로서비스 아키텍처(MSA)를 도입하고 API Gateway까지 앞에 세웠습니다. 이제 클라이언트의 모든 트래픽은 Gateway가 yml 설정에 적힌 IP(192.168.0.10:8082)를 보고 주문 서버로 넘겨줍니다. 그런데 오늘 블록버스터급 할인 이벤트가 터지면서 주문 서버 CPU가 99%를 치기 시작했습니다! 다급해진 인프라 팀이 AWS 오토스케일링(Auto Scaling)을 돌려 주문 서버를 순식간에 5대로 늘렸습니다. 새로 켜진 서버 4대는 각자 랜덤한 새 IP(예: 192.168.0.11 ~ 14)를 발급받았습니다. 자, Gateway는 새로 켜진 4대의 서버 IP를 어떻게 알고 트래픽을 골고루 나눠줄(Load Balancing) 수 있을까요? yml 파일에 직접 적혀있지 않으니 Gate..
[Spring Boot] Spring Cloud Gateway 하나의 거대한 프로젝트(Monolithic)로 시작했던 쇼핑몰이 대박을 터뜨렸습니다. 개발자가 100명이 넘어가자, 코드가 꼬이고 배포 한 번 하려면 30분이 걸립니다. 결국 넷플릭스(Netflix)나 우아한형제들처럼 시스템을 '회원(Auth)', '상품(Product)', '주문(Order)' 서버로 잘게 쪼개는 마이크로서비스 아키텍처(MSA, Microservice Architecture)로 전환했습니다. 그런데 문제가 생겼습니다. 프론트엔드 앱이 회원가입을 하려면 auth.api.com을 찔러야 하고, 상품을 조회하려면 product.api.com을 찔러야 합니다. 클라이언트가 수십 개의 분산된 서버 주소를 전부 외우고 다녀야 하는 끔찍한 상황입니다. 게다가 JWT 토큰 검증은 30개의 서버마다 각..
[Spring Boot] ELK 스택 (Elasticsearch, Logstash, Kibana): 중앙 집중형 로그 파이프라인 여러분이 성공적인 서비스를 만들어 서버가 50대로 늘어났습니다. 어느 날 밤, 고객센터에서 "결제가 자꾸 실패한다고 고객 클레임이 들어옵니다!"라는 다급한 연락이 옵니다. 초보 개발자는 리눅스(Linux) 터미널을 열고 1번 서버에 접속해 tail -f /var/log/spring.log를 치고 한참 찾다가 없으면 2번 서버에 들어가서 또 찾습니다. 50대의 서버를 일일이 돌아다니며 에러 로그를 찾는 사이에 고객은 다 떠나고 회사는 망할 것입니다. 수십 대의 서버에서 각자 뿜어내는 수 기가바이트(GB)의 텍스트 로그 파일들을 한 곳으로 빨아들여서, "결제 에러"라는 키워드 하나만 치면 구글처럼 0.1초 만에 50대 서버 전체의 에러 로그를 찾아주고 삐까뻔쩍한 대시보드로 시각화해 주는 인프라는 없을까요? ..
[Spring Boot] Elasticsearch 연동: RDB의 LIKE 검색 한계를 부수다 여러분이 당근마켓 같은 중고 거래 플랫폼을 만들었다고 가정해 봅시다. 데이터베이스(MySQL)에 등록된 상품 제목이 1,000만 개입니다. 사용자가 검색창에 "시원한 여름용 원피스"라고 검색했습니다. 일반적인 자바 개발자는 무심코 SELECT * FROM product WHERE title LIKE '%시원한 여름용 원피스%'라는 쿼리를 날립니다. 이 쿼리(퍼지 검색)는 인덱스(Index, 색인)를 타지 못하고 1,000만 개의 테이블 전체를 처음부터 끝까지 하나하나 스캔(Full Table Scan)해야 하므로 결과가 나오기까지 5초~10초가 걸리며 DB CPU가 터져버립니다. 게다가 사용자가 "여름 원피스"라고 띄어쓰기를 조금만 다르게 해도 결과가 0건으로 안 나옵니다! 관계형 데이터베이스(RDB)는..