google.com 같은 사이트에 접속할 때 실제로 무슨 일이 벌어지는지를 이해하기 위한 오늘의 주제. API 네트워크 단계를 정리해보았다.
[네트워크]
DNS → TCP → HTTPS → index.html 다운로드
↓
[브라우저]
HTML 파싱 → JS 번들 다운로드
↓
[자바스크립트]
React/Vue 앱 실행
↓
[API 네트워크]
백엔드 API 호출
↓
[렌더링]
DOM 생성 → 화면에 UI 표시
사용자가 화면의 버튼을 누르거나 페이지가 로드되면, 프론트엔드 코드(React/Vue)는 fetch나 Axios 같은 라이브러리를 사용하여 백엔드 서버에 HTTP 요청
이 단계에서 가장 많이 마주치는 에러가 바로 CORS(Cross-Origin Resource Sharing) 에러
백엔드에서 다음 세 가지를 허용해줘야 브라우저가 안심하고 데이터를 가져감
- Allow Origin: 허용할 도메인 (예: https://my-frontend.com)
- Allow Methods: 허용할 HTTP 메서드 (GET, POST, PUT, DELETE 등)
- Allow Headers: 허용할 헤더 (인증을 위한 Authorization, Content-Type 등)
1) 어노테이션 방식 (특정 컨트롤러만 허용)
특정 API만 외부에서 접근하게 하고 싶을 때 사용합니다.
Java
@RestController
@RequestMapping("/api")
@CrossOrigin(origins = "http://localhost:3000") // 특정 도메인만 허용
public class ApiController {
// ...
}
2) 전역 설정 방식 (가장 권장됨)
프로젝트 전체에 일관된 규칙을 적용할 때 사용. WebMvcConfigurer를 구현.
Java
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**") // 모든 경로에 대해
.allowedOrigins("https://my-frontend.com", "http://localhost:3000") // 허용 도메인
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") // 허용 메서드
.allowedHeaders("*") // 모든 헤더 허용
.allowCredentials(true) // 쿠키/인증 정보를 포함할지 여부
.maxAge(3600); // Preflight 요청을 캐싱할 시간 (초 단위)
}
}
3) Preflight(OPTIONS) 요청과 성능 -> prefilght에 대한 설명은 다음 3번에서 설명
Preflight는 매번 발생하면 네트워크 비용이 듦
4) 실무 주의사항
(Wildcard) 자제: 테스트 환경에서는 allowedOrigins("*")가 편하지만, 보안상 실제 서비스에서는 허용할 도메인을 명확히 적어주는 것이 바람직
Credentials 주의: allowCredentials(true)를 설정했다면, allowedOrigins에 *를 쓸 수 없다. 반드시 구체적인 도메인을 명시해야 함
Spring Security: 만약 프로젝트에 Spring Security를 사용 중이라면, WebMvc 설정 외에 Security 필터 체인에서도 CORS 설정을 별도로 해줘야 에러가 나지 않음
브라우저는 실제 데이터를 요청하기 전에, 안전한지 확인하기 위해 OPTIONS 메서드로 미리 한 번 확인.
- Controller: 요청을 받아 파라미터를 검증
- Service: 비즈니스 로직을 처리
- Repository: DB에 접속하여 데이터를 조회하거나 수정
- 응답: 처리 결과를 200 OK 또는 404 Not Found 같은 상태 코드와 함께 JSON으로 프론트엔드에 반환
백엔드 개발자에게 캐싱(Caching)은 시스템의 성능과 확장성을 결정짓는 핵심 무기. API 호출에서 백엔드가 매번 무거운 DB 조회를 하지 않고 데이터를 빠르게 돌려주는 전략은?
캐싱은 전체 요청의 80%는 20%의 데이터에 집중된다는 원리에 기반한다. 자주 찾는 데이터(Hot Data)를 느린 저장소(Disk/DB)가 아닌 빠른 저장소(Memory)에 미리 두는 것이 핵심
읽기 전략 (Read Strategy)
Look Aside (가장 일반적):
- 앱이 먼저 캐시를 확인
- 데이터가 없으면(Cache Miss) DB에서 가져와 캐시에 저장하고 응답.
- 장점: 캐시 서버가 죽어도 DB를 통해 서비스가 유지됨
Read Through:
- 앱은 캐시 서버에서만 데이터를 읽는다.
- 캐시가 직접 DB에서 데이터를 채워준다.
쓰기 전략 (Write Strategy)
Write Around:
- 데이터를 DB에만 쓴다. 캐시는 읽기 요청이 올 때만 업데이트
- 단점: 캐시와 DB의 데이터가 다를 수 있는 기간이 생김
Write Back:
- 데이터를 캐시에 먼저 모아서 쓰고, 나중에 한꺼번에 DB에 반영
- 장점: 쓰기 작업이 많은 서비스(로그, 좋아요 수 등)에서 DB 부하를 획기적으로 줄인다.
- 단점: 캐시 서버가 장애 나면 데이터가 손실될 위험이 있다.
캐시는 무한정 쌓아둘 수 없으므로 "언제 지울 것인가"가 중요.
TTL (Time To Live): 특정 시간이 지나면 자동으로 삭제 (예: 공지사항 1시간).
LRU (Least Recently Used): 가장 오랫동안 사용되지 않은 데이터부터 삭제.
LFU (Least Frequently Used): 사용 빈도가 가장 낮은 데이터부터 삭제.