본문 바로가기

분류 전체보기

(136)
[Spring Boot] 동시성 제어의 절대 반지 - 낙관적 락(Optimistic Lock) vs 비관적 락(Pessimistic Lock)의 철학과 분산 환경 재시도(Retry) 메커니즘 지금까지 우리는 조회(Read) 쿼리를 어떻게든 최적화하여 10만 명의 조회를 0.1초 만에 처리하는 극강의 성능 아키텍처를 만들어냈습니다. 하지만 데이터베이스 세계에서 가장 무섭고 까다로운 재앙은 조회할 때 터지는 것이 아니라, 여러 명의 유저가 "동시에 하나의 데이터를 수정(Write/Update)하려 달려들 때" 터집니다.유명 가수의 콘서트 티켓팅이나 한정판 명품 선착순 판매(Draw) 이벤트가 열렸다고 상상해 봅시다. 남은 재고는 딱 1개입니다. 그런데 A 유저와 B 유저가 0.001초의 오차도 없이 동시에 결제 버튼을 누릅니다. 두 명의 톰캣 쓰레드는 DB에서 남은 재고가 1개인 것을 동시에 확인(SELECT)하고, 각자 재고를 0으로 수정(UPDATE)한 뒤 커밋해 버립니다. 어? 재고는 1개..
[Spring Boot] QueryDSL 심화 - 동적 쿼리, 서브 쿼리(Sub-Query), 그리고 튜플(Tuple) 추출과 DTO 프로젝션 성능 최적화의 끝판왕 우리는 지난 58단계를 통해 JPA의 고질적인 퍼포먼스 버그인 N+1 문제를 @BatchSize 설정 하나로 우아하게 부숴버렸습니다. 정적인 조회 쿼리, 즉 검색 조건이 딱 정해져 있는 findAllByUsername(String username) 같은 쿼리는 스프링 데이터 JPA가 제공하는 메서드 이름명명 규칙(Query Method)이나 @Query 애노테이션으로 작성한 JPQL만으로도 충분히 아름답게 처리할 수 있습니다.하지만 여러분이 실무에서 마주하게 될 진짜 난관은 '어드민 백오피스(Admin Backoffice)' 화면입니다. 검색 폼에 아이디, 이름, 휴대폰 번호, 주문 날짜 범위, 회원 등급 등 무려 10개가 넘는 검색 필터 조건이 존재하고, 관리자가 이 중 몇 개의 조건을 입력할지 실행 ..
[Spring Boot] JPA N+1 문제의 완전한 종결 - Fetch Join의 함정과 EntityGraph, @BatchSize 심화 실무 튜닝 가이드 "어? 분명히 컨트롤러에서 teamRepository.findAll() 메서드 단 하나만 깔끔하게 호출했는데, 콘솔 창을 보니 SELECT 쿼리가 왜 101개나 미친 듯이 우수수 쏟아지는 거지?! 내 서버 해킹당했나?!" 스프링 데이터 JPA를 처음 접하는 1~2년 차 주니어 개발자들이 가장 먼저 마주하게 되는 거대한 절망의 벽. 그리고 백엔드 기술 면접관이 단골로 물어보지만, 대다수의 지원자가 "연관관계 매핑할 때 지연 로딩(LAZY)을 쓰면 해결되는 거 아니었나요?"라며 기초적인 헛소리를 하다가 광탈하게 만드는 최악의 성능 저하 원흉. 바로 전설적인 'N+1 문제(N+1 Problem)'입니다. 구글링을 해보면 수많은 기술 블로그들이 N+1 문제의 유일한 해결책으로 단순히 Fetch Join을 사용하..
[Spring Boot] 다중 DB 트랜잭션의 딜레마 - JTA(2PC) 글로벌 트랜잭션과 ChainedTransactionManager의 치명적 한계, 그리고 SAGA 패턴으로의 진화 여러분이 다니는 회사의 비즈니스가 급성장하면서, 10년 동안 온갖 레거시(Legacy) 데이터가 꼬여있는 기존의 오라클(Oracle) DB 서버로는 한계를 느끼기 시작했습니다. 그래서 새로운 마이크로서비스(MSA) 모듈을 런칭할 때는 최신의 트렌디한 MySQL 8.0(또는 PostgreSQL)을 도입하기로 결정했습니다. 축하합니다! 이제 여러분이 담당하는 스프링 부트 애플리케이션은 두 개의 완전히 다른 이기종 데이터베이스(Heterogeneous Database)와 동시에 커넥션을 맺고 통신해야 합니다. 여기까지는 단순히 application.yml에 DataSource를 두 개 적으면 되는 쉬운 일입니다. 그런데 여기서 백엔드 시스템의 데이터 무결성을 파괴하고 개발자들의 등골을 오싹하게 만드는 최악의 ..
[Spring Boot] DB Replication (Master-Slave) - AbstractRoutingDataSource 라우팅을 통한 읽기/쓰기 분리(Read/Write Splitting) 마스터 아키텍처 완벽 구현 가이드 우리는 지금까지 단 하나의 물리적인 데이터베이스(DB) 인스턴스만을 바라보고 스프링 부트 코딩을 해왔습니다. 트래픽이 적은 토이 프로젝트나 B2B 인트라넷 서비스일 때는 아무런 문제가 발생하지 않습니다. 하지만 서비스가 폭발적으로 성장하여 동시 접속자가 1만 명, 10만 명을 돌파하는 순간, 단일 DB는 엄청난 비명을 지르며 서버가 통째로 뻗어버리게 됩니다.백엔드 개발자가 아무리 애플리케이션 단(WAS)에서 Redis 로컬 캐싱(Cache)을 바르고 JPQL 쿼리를 미친 듯이 튜닝해도, 본질적으로 수만 건의 SELECT 조회 요청과 수천 건의 INSERT/UPDATE 쓰기 요청이 단 하나의 DB 하드웨어 자원(CPU 연산, Disk I/O, 메모리 버퍼)을 갉아먹으면 병목(Bottleneck) 현상은 물..
[Spring Boot] 카오스 엔지니어링 (Chaos Engineering) - 프로덕션 환경에 원숭이를 풀어 고의로 장애를 유발하고 서버의 회복력(Resiliency) 테스트하기 우리는 지난 54단계까지 단위 테스트(BDD), 통합 테스트(Testcontainers 격리), 커버리지 강제 검증 게이트(Jacoco/SonarQube), 그리고 극한의 성능 테스트(JMeter/nGrinder)라는 완벽한 4중 방어막을 구축했습니다. 이제 내 코드는 논리적으로 버그가 0개이며, 초당 1만 건의 트래픽(TPS)을 가뿐히 버텨냅니다. 완벽하죠? 하지만 실무의 거친 프로덕션(Production) 세계는 당신의 논리 정연한 코드와 테스트 결과를 비웃습니다.클라우드 벤더사(AWS, GCP)의 데이터센터 스위치가 갑자기 벼락을 맞아 타버려서 DB와의 네트워크가 5초간 단절된다면? 우리가 결제를 연동 중인 외부 PG사(카카오페이, 토스)의 API 서버가 트래픽 폭주로 인해 갑자기 500 에러를 뿜..
[Spring Boot] 성능 테스트 (Performance Test) 심화 - JMeter와 nGrinder로 서버 임계점(TPS) 추적하고 병목(Bottleneck) 완벽 타파하기 대규모 마케팅 선착순 이벤트 오픈 전날 밤, 텅 빈 서버에 혼자 조용히 접속해 결제 버튼을 누르며 "오, 응답 속도가 0.1초네! 서버 개발 진짜 완벽하게 잘했다!"라고 자화자찬하는 주니어 백엔드 개발자의 안도감은, 다음 날 오전 10시 정각 이벤트 오픈 5분 만에 지옥의 비명으로 바뀝니다. 선착순 쿠폰을 받기 위해 1만 명의 유저가 동시에 접속하여 광클을 시작하는 순간, 스프링 부트 내장 톰캣(Tomcat)의 쓰레드 풀(Thread Pool)이 단 1초 만에 꽉 차버리고, DB 커넥션 풀(HikariCP)이 고갈되며, 결국 화면에는 "502 Bad Gateway" 혹은 "504 Gateway Timeout"이라는 처참한 메시지와 함께 서버가 통째로 기절해 버리기 때문입니다.실무 백엔드 엔지니어의 진짜 ..
[Spring Boot] Jacoco와 SonarQube - 코드 커버리지 80%의 벽과 정적 분석으로 레거시 악취(Code Smell) 완벽 제거하기 앞선 51, 52단계 포스팅을 통해 우리는 Testcontainers의 DB 격리와 BDD 철학, Mockito를 활용하여 훌륭하고 빈틈없는 단위 테스트 코드를 작성하는 법을 배웠습니다. 하지만 개발팀의 규모가 10명, 50명으로 커지고 코드가 거대해지면, 누군가는 반드시 바쁘다는 핑계를 대거나 귀찮다는 이유로 테스트를 짜지 않고 슬쩍 프로덕션 브랜치(Branch)로 코드를 병합(Merge)하려 들 것입니다. "김 대리님, 이번에 PR(Pull Request) 올리신 코드, 테스트는 꼼꼼하게 다 작성하셨죠?"라는 사람의 말과 양심에 의존하는 검증은 더 이상 통하지 않습니다. 수동 코드 리뷰(Code Review) 과정에서 인간의 눈으로 수만 줄의 PR을 훑어보며 테스트가 빠진 엣지 케이스를 모두 찾아내는..
[Spring Boot] JUnit 5와 Mockito 심화 - BDD 철학과 엣지 케이스를 뚫어내는 단위 테스트의 정석과 실무 노하우 개발자들이 모인 컨퍼런스나 술자리에서 "TDD(Test-Driven Development, 테스트 주도 개발)를 실무에서 엄격하게 지키시나요?"라는 질문이 나오면 십중팔구 깊은 한숨과 변명이 쏟아집니다. "스타트업이라 일정이 너무 바빠서요", "기획과 요구사항이 매일같이 뒤집어지는데 테스트부터 짜면 코드를 두 번씩 고쳐야 해요". 네, 맞습니다. 실무의 치열하고 급변하는 비즈니스 환경에서 모든 코드를 교과서적인 TDD 방식(Red-Green-Refactor)으로 짜는 것은 이상론에 가깝습니다. 하지만 그렇다고 해서 '단위 테스트(Unit Test)' 자체를 작성하는 것을 포기하는 것은 개발자로서의 책임을 방기하는 죄악입니다. 내가 심혈을 기울여 짠 결제 로직이 장바구니에 중복 쿠폰을 먹이고 마일리지를 최..
[Spring Boot] Spring Boot Test 격리 - @SpringBootTest의 재앙과 Testcontainers를 이용한 완벽한 DB 테스트 구축 우리는 지난 50개의 포스팅을 통해 스프링 부트 생태계의 거대한 아키텍처와 다양한 기술 스택들을 낱낱이 해부하며 애플리케이션을 완성해 왔습니다. 하지만 현업에서 모든 비즈니스 로직을 다 짜고 나서 "기능 개발 다 끝났습니다!"라고 당당하게 말하는 주니어 개발자에게, 산전수전 다 겪은 시니어 개발자나 테크 리드가 가장 먼저 던지는 싸늘한 질문은 단 하나입니다."그래서, 테스트 코드는 다 작성하셨나요?"수만 줄의 코드가 유기적으로 얽혀있는 실무의 거대한 마이크로서비스(MSA) 환경이나 대형 모놀리식(Monolithic) 환경에서, 자동화된 테스트 코드가 없는 애플리케이션은 브레이크가 고장 난 상태로 시속 200km로 달리는 스포츠카와 같습니다. 언제 어디서 누구의 코드와 충돌하여 대폭발을 일으킬지 모르는 시..
[Spring Boot] Java 21 Virtual Threads (가상 쓰레드): 패러다임의 혁명과 시리즈의 피날레 우리는 앞서 29단계에서 대규모 트래픽을 감당하기 위해 WebFlux(리액티브 프로그래밍)를 배웠습니다. 톰캣(Tomcat)의 쓰레드가 DB 응답을 기다리며 멍때리는(Blocking) 현상을 없애기 위해, 코드를 Mono와 Flux라는 상자로 감싸고 flatMap으로 떡칠하는 기괴한 비동기 코드를 짜야만 했습니다. 러닝 커브는 끔찍했고 레거시 JPA와의 궁합은 최악이었습니다. 백엔드 개발자들은 눈물을 흘리며 외쳤습니다. "아니, 코드는 옛날처럼 위에서 아래로 읽히는 평범한 동기식(Blocking)으로 짜면서, 멍때릴 때 쓰레드 낭비만 안 하게 해주는 마법은 없나요?!" 자바를 만든 오라클(Oracle)은 10년 넘게 진행된 프로젝트 룸(Project Loom)을 통해 마침내 해답을 내놓았습니다. "운영체..
[Spring Boot] SonarQube (소나큐브): 코드 퀄리티를 감시하는 무자비한 경찰관 지금까지 우리는 스프링 부트의 온갖 심화 기술과 아키텍처를 총동원하여 서비스를 만들었고, 완벽한 테스트 코드(Testcontainers)까지 짜서 동작을 보증했습니다. 그런데 '동작하는 코드'가 곧 '좋은 코드'일까요? 후임 개발자가 들어와서 메서드 하나를 1,000줄짜리 스파게티로 짜놓고, 하드코딩된 비밀번호를 소스에 박아두고, 아무도 안 쓰는 쓰레기 변수(Dead Code)를 잔뜩 남겨둔 채 테스트만 턱걸이로 통과시켜서 배포를 요청했습니다. 바쁜 시니어 개발자가 눈으로 수천 줄의 PR(Pull Request)을 읽으며 이 악취(Code Smell) 나는 코드를 다 걸러낼 수 있을까요? "코드를 실행시키지 않고(Static), 그냥 텍스트 자체만 쓱 훑어보고 논리적 오류, 복잡도, 보안 취약점을 완벽하..
[Spring Boot] Testcontainers: 완벽히 고립된 통합 테스트의 마법 "분명히 내 컴퓨터에선 테스트가 100% 통과했는데, 젠킨스(CI)에 올리기만 하면 빌드가 실패해요!" 개발팀에서 하루가 멀다고 들리는 비명입니다. 20단계에서 배운 단위 테스트(Unit Test)는 순수한 자바 코드만 검증하므로 환경을 타지 않지만, 실제 쿼리가 날아가는지, Redis에 값이 잘 꽂히는지 확인하는 통합 테스트(Integration Test, @SpringBootTest)는 외부 인프라(MySQL, Redis)의 상태에 극도로 의존합니다. 철수의 노트북엔 MySQL 8.0이 있고 영희의 노트북엔 MySQL 5.7이 깔려있으며, 젠킨스 서버에는 아예 DB가 안 깔려있다면? 이 '테스트 환경 불일치'를 해결하기 위해 과거에는 H2 인메모리 DB로 흉내를 내봤지만, H2는 실제 MySQL 전용..
[Spring Boot] Flyway (플라이웨이): 데이터베이스 형상 관리의 정석 주니어 개발자들의 배포 날 밤, 가장 많이 터지는 대참사가 무엇일까요? 바로 "자바 코드는 최신으로 배포했는데, 운영 DB에 컬럼(Column) 추가하는 걸 깜빡해서 서버가 죽는 현상"입니다. JPA hibernate.ddl-auto=update 기능이 있긴 하지만, 실무 운영 서버에서 이 옵션을 켜는 것은 "내 손으로 DB를 날려먹고 사직서를 쓰겠다"는 선언과 같습니다(절대 사용 금지). 결국 개발자가 수동으로 ALTER TABLE 쿼리를 짜서 DBA에게 넘겨주거나 직접 날려야 하는데, 이 과정에서 사람이 실수하거나 쿼리를 누락하면 걷잡을 수 없는 재앙이 벌어집니다. "소스코드는 Git으로 1.0, 1.1 버전을 깔끔하게 관리하면서, 왜 데이터베이스 스키마(DDL)는 원시인처럼 구두로 관리하는가? DB..
[Spring Boot] 멀티 모듈(Multi-Module) 아키텍처: 거대해진 모놀리스의 분리 작은 쇼핑몰로 시작했던 우리의 스프링 부트 프로젝트가 3년 만에 거대해졌습니다. 처음에는 src/main/java 폴더 하나에 컨트롤러(API), 도메인(Entity), 그리고 새벽에 도는 배치(Batch) 코드까지 몽땅 때려 넣고 행복하게 개발했습니다. 그런데 어느 날, 배치 팀 개발자가 User 엔티티에 필드를 하나 추가하고 배포를 했더니, 갑자기 API 팀의 로그인 서버가 런타임 에러를 뿜으며 죽어버립니다. "아니, 나는 배치 코드만 고쳤는데 왜 API 서버가 죽어?!" 하나의 거대한 프로젝트(모놀리식) 안에서 모든 코드가 뒤엉켜버린 스파게티 의존성의 저주입니다. 그렇다고 당장 38단계에서 배운 MSA(마이크로서비스)로 완전히 찢자니 서버 인프라 비용과 관리의 압박이 너무 심합니다. "하나의 프로젝..
[Spring Boot] WebRTC 화상 회의: P2P 시그널링 서버의 완성 여러분이 당근마켓의 개발자인데, 사용자들이 중고 물품을 실시간으로 보여주며 대화할 수 있는 '1:1 영상 통화' 기능을 만들어 달라는 미션을 받았습니다. 일반적인 생각으로는 철수의 스마트폰 카메라(영상) 데이터를 스프링 부트 서버로 쏘고, 서버가 그걸 다시 영희에게 쏴주는(Relay) 방식을 떠올립니다. 하지만 영상 데이터는 1초에 수십 메가바이트(MB)를 차지합니다. 1,000명이 동시에 영상 통화를 한다면 서버의 네트워크 대역폭(Bandwidth) 요금만 한 달에 수억 원이 청구될 것이며 서버는 펑 하고 터질 것입니다. "서버를 거치지 않고, 철수 폰에서 영희 폰으로 직접(Peer-to-Peer, P2P) 영상 데이터를 쏴버릴 수는 없을까?" 이 미친 효율의 혁신적인 기술이 바로 웹 브라우저 간의 다..
[CSS] 웹 프론트엔드 CSS 총정리 및 학습 로드맵CSS 실전 웹 UI 프로젝트 (모달, 스크롤, SEO 최적화) CSS 36부작 에필로그 - 레이아웃의 진화 역사와 CSS가 맞이할 위대한 미래 로드맵대장정이 끝났습니다. 브라우저가 기본적으로 제공하는 끔찍한 파란색 굴림체 링크 밑줄을 없애기 위해 text-decoration: none을 적어 넣던 아주 기초적인 첫 포스팅부터 시작하여, Box Model의 물리적 크기를 뜯어보고, display: flex와 display: grid로 1차원과 2차원 바둑판 레이아웃을 정복했으며, z-index 9999의 비극인 Stacking Context를 격파했습니다. 그리고 Tailwind CSS, Styled-components 같은 현대 모던 프론트엔드의 거대한 패러다임 전환과 .sr-only를 통한 구글 SEO/접근성 최적화에 이르기까지, 여러분은 무려 35개의 거대한 프..
[CSS] CSS 실전 웹 UI 프로젝트 (모달, 스크롤, SEO 최적화) CSS Scroll Snap & Behavior - 자바스크립트를 버리고 틱톡(TikTok) 뷰어와 스무스 스크롤을 창조하다웹사이트 최상단에 있는 네비게이션 바에서 'Contact Us(오시는 길)' 메뉴를 클릭했을 때, 페이지 맨 밑바닥까지 부드럽게 미끄러지듯 스크롤되어 내려가는 유려한 '스무스 스크롤(Smooth Scroll)' 효과를 기억하십니까? 불과 5년 전만 하더라도 이 기능을 구현하려면 jQuery 라이브러리를 통째로 임포트(Import)하고 animate({scrollTop: ...})라는 수십 줄의 자바스크립트 코드를 짜야만 했습니다. 또한 화면을 스와이프할 때마다 자석처럼 다음 사진으로 찰칵! 하고 화면이 딱딱 정렬되어 멈추는 넷플릭스의 가로 캐러셀(Carousel)이나 틱톡(TikTo..
[CSS] 성능 최적화와 디버깅 크로스 브라우징과 벤더 프리픽스 (Vendor Prefix)이번 포스팅에서는 [CSS 실무 유지보수] 파트의 세부 주제인 구형 브라우저 대응과 지원 기능 판별에 대하여 상세히 알아보겠습니다.1. 벤더 프리픽스의 원리와 자동화 도구최신 CSS 스펙이 공식 표준으로 채택되기 전, 브라우저 제조사들이 자신들의 엔진에 실험적으로 기능을 구현할 때 접두사를 붙입니다. (-webkit-: Chrome/Safari, -moz-: Firefox, -ms-: IE/Edge)이런 것들을 일일이 손으로 쓰는 것은 불가능에 가깝습니다. 실무에서는 반드시 Autoprefixer(PostCSS 플러그인) 같은 도구를 빌드 파이프라인에 연결하여 코딩 시에는 표준 CSS만 작성하도록 자동화해야 합니다.2. 조건문(Feature Qu..
[CSS] 현대적인 CSS 프레임워크와 도구 Tailwind CSS 프레임워크 - 유틸리티 퍼스트(Utility-First) 혁명, CSS 파일을 삭제하다지난 수십 년 동안 웹 개발자들은 암묵적인 절대 규칙(국룰) 하나를 목숨처럼 지켜왔습니다. "HTML은 문서의 뼈대만 짜고, CSS 파일은 철저하게 분리(Separation of Concerns)해서 디자인을 따로 입혀라." 이 원칙 때문에 우리는 HTML 요소에 class="card-profile" 같은 의미론적(Semantic)인 예쁜 이름을 지어주고, 다시 style.css 파일을 열어 .card-profile { border-radius: 8px; box-shadow: ... }라고 길고 지루한 스타일을 앵무새처럼 반복해서 타이핑해야 했습니다. 파일 두 개를 번갈아 열어가며 코딩하는 피로감..
[CSS] CSS 아키텍처와 방법론 (BEM, Sass/SCSS) CSS 방법론(Architecture) - BEM과 OOCSS, 덮어쓰기 지옥에서 살아남는 클래스 네이밍의 예술프로그래머 역사상 가장 해결하기 어려운 두 가지 난제는 "캐시 무효화(Cache Invalidation)"와 "이름 짓기(Naming Things)"라는 유명한 격언이 있습니다. HTML/CSS를 처음 배우는 초보자들은 .btn, .box, .title처럼 직관적이고 짧은 클래스 이름을 짓는 데 행복을 느낍니다. 하지만 실무에 투입되어 수만 줄의 CSS가 얽혀있는 대규모 프로젝트를 마주하게 되면, 이 짧고 낭만적인 클래스 이름들은 끔찍한 재앙의 씨앗이 됩니다. 어제 로그인 페이지의 .title 글씨를 빨간색으로 바꿨더니, 오늘 메인 페이지의 상품 목록 .title 마저 모조리 피눈물을 흘리며 빨..
[CSS] CSS 변수(Variables)와 테마 관리 CSS Variables (사용자 지정 속성) - 하드코딩의 늪에서 벗어나 동적 테마를 지배하다과거 웹 개발자들은 웹사이트 전체의 메인 컬러(예: 브랜드 컬러인 파란색 #1da1f2)를 적용하기 위해 수백 개의 CSS 클래스에 color: #1da1f2;를 복사해서 붙여넣었습니다. 어느 날 기획자가 "브랜드 컬러를 약간 보라색 톤으로 바꿔주세요"라고 요청하면, 개발자는 프로젝트 전체를 뒤져가며 수백 개의 코드를 일일이 찾아 바꾸는(Find & Replace) 지옥 같은 막노동을 해야만 했습니다. 프로그래밍의 제1 원칙인 DRY(Don't Repeat Yourself, 중복 금지)가 완전히 박살 난 상태였죠.이를 해결하기 위해 Sass(SCSS)나 Less 같은 CSS 전처리기(Preprocessor)들이..
[CSS] CSS 애니메이션 완벽 가이드 (Transform, Transition, Keyframes) CSS Transition(트랜지션) - 뚝뚝 끊기는 화면에 '시간'의 부드러움을 불어넣는 마술자바스크립트(JS)의 화려함 없이, 오직 CSS와 HTML만으로 짜인 웹사이트를 처음 배포했을 때의 기억을 떠올려 보십시오. 파란색 버튼 위에 마우스를 :hover 하는 순간 배경색이 빨간색으로 바뀌도록 코드를 짰을 것입니다. 코드는 완벽하게 동작하지만, 결과물은 어딘가 묘하게 촌스럽고 투박하게 느껴집니다. 왜 그럴까요? 마우스가 올라가는 0.001초의 찰나에 파란색이 빨간색으로 '팍!' 하고 점프하듯 변해버리기 때문입니다. 현실 세계에서 사물이 이동하거나 변할 때는 무조건 '시간(Time)'이 걸립니다.빛조차도 지구에 도달하는 데 8분이 걸리는데, 브라우저 화면의 픽셀이 시간의 흐름 없이 순간 이동(Telep..
[CSS] 시각적 효과와 꾸미기 (그림자, 필터, 다크 모드) CSS 시각 효과 - 그림자(Shadow)와 필터(Filter)로 2D 화면에 3D 공간감을 불어넣다우리가 매일 쳐다보는 모니터나 스마트폰 화면은 물리적으로 완벽하게 평평한 2D(2차원) 평면입니다. 하지만 애플(Apple)의 iOS UI나 구글의 머티리얼 디자인(Material Design)을 보면, 버튼들이 공중에 살짝 떠 있는 것 같기도 하고, 뒤에 있는 배경이 반투명한 유리창(Glassmorphism)을 댄 것처럼 은은하게 번져 보이기도 합니다. 어떻게 물리적인 평면 디스플레이에서 이런 깊이감(Depth)과 3차원적 질감을 느끼게 할 수 있을까요? 그것은 바로 프론트엔드 개발자들이 박스 주변에 미세한 '그림자(Shadow)'를 흩뿌리고, 요소 전체에 포토샵 뺨치는 '필터(Filter)' 마법을 걸..