-
JVM(Java Virtual Machine)μμλ λ΄λΆμ μΌλ‘ μ¬λ¬ μ’ λ₯μ μ€ν(stack) μ μ¬μ©ν©λλ€. κ°κ°μ μ€νμ JVMμ μ€ν λ° λ©λͺ¨λ¦¬ κ΄λ¦¬ λ±μμ μ€μν μν μ ν©λλ€. μ£Όμ μ€ν μ’ λ₯λ λ€μκ³Ό κ°μ΅λλ€:
π§ 1. Java Virtual Machine Stack (λλ Thread Stack)
- μ€λͺ : κ° μ€λ λ(thread) λ§λ€ νλμ© μ‘΄μ¬νλ μ€νμ λλ€.
- μν : λ©μλ νΈμΆ μ μμ±λλ μ€ν νλ μ(stack frame) μ μ μ₯ν©λλ€. κ° νλ μμλ:
- μ§μ λ³μ(Local Variables)
- νΌμ°μ°μ μ€ν(Operand Stack)
- λ©μλ λ°ν μ£Όμ λ±μ΄ ν¬ν¨λ¨
- μμΈ: μ€ν μ€λ²νλ‘μ° λ°μ μ
StackOverflowErrorλ°μ
π 2. Native Method Stack
- μ€λͺ : λ€μ΄ν°λΈ λ©μλ(Cλ‘ κ΅¬νλ JNI λ©μλ λ±) νΈμΆ μ μ¬μ©λλ μ€νμ λλ€.
- μν : C/C++ λ± JVM μΈλΆ μΈμ΄λ‘ ꡬνλ λ©μλμμ νμν λ°μ΄ν°λ₯Ό μ μ₯
- νΉμ΄μ¬ν: μΌλΆ JVM ꡬνμμλ Java μ€νκ³Ό ν΅ν©λκΈ°λ ν¨
πΎ 3. Operand Stack (νΌμ°μ°μ μ€ν)
- μ€λͺ : μμ JVM Stackμ κ° μ€ν νλ μ λ΄μ μ‘΄μ¬νλ μ€νμ λλ€.
- μν : λ°μ΄νΈμ½λ λͺ λ Ήμ΄κ° μ€ν μ€μΈ μ€κ° μ°μ° κ²°κ³Όλ₯Ό μμλ‘ μ μ₯
- μμ:
iadd,imulκ°μ λ°μ΄νΈμ½λ λͺ λ Ήμ μ΄ μ€νμμ νΌμ°μ°μλ₯Ό κΊΌλ΄ κ³μ°
π§± 4. Call Stack (νΈμΆ μ€ν)
- μ€λͺ : JVM Stackμ ꡬ쑰μ μΈ μΈ‘λ©΄μ μ§μΉνλ μ©μ΄λ‘, λ©μλ νΈμΆμ νλ¦μ λνλ λλ€.
- μν : νμ¬ μ€ν μ€μΈ λ©μλλΆν° μμν΄ νΈμΆλ λ©μλλ€μ΄ 차곑차곑 μμ¬ μμ
- νΉμ§: λλ²κΉ μ “Stack Trace"λ‘ νμΈ κ°λ₯
μΆκ°λ‘ JVMμλ μ€ν μ΄μΈμλ λ€μκ³Ό κ°μ λ©λͺ¨λ¦¬ μμμ΄ μμ΅λλ€:
CreatedTue, 01 Apr 2025 15:44:09 +0900 -
JVM(Java Virtual Machine)μμ heap λ©λͺ¨λ¦¬λ μλ° μ ν리μΌμ΄μ μ΄ λ°νμ μ λμ μΌλ‘ μμ±νλ κ°μ²΄λ€μ΄ μ μ₯λλ μμμ λλ€. μ¦,
newν€μλλ₯Ό μ¬μ©ν΄μ μμ±νλ λλΆλΆμ κ°μ²΄λ€μ΄ μ΄ μμμ μ μ₯λ©λλ€.
π Heap λ©λͺ¨λ¦¬ ꡬμ±
Heapμ ν¬κ² λ€μ λ μμμΌλ‘ λλ©λλ€:
- Young Generation (Young Gen): μλ‘ μμ±λ κ°μ²΄κ° μ μ₯λλ κ³³.
- Eden: λλΆλΆμ μ κ°μ²΄κ° μ¬κΈ°μ μμ±λ¨.
- Survivor: Edenμμ μ΄μλ¨μ κ°μ²΄λ€μ΄ μ΄λ.
- Old Generation (Tenured Gen): Young Genμ κ±°μ³ μ€λ μ΄μλ¨μ κ°μ²΄λ€μ΄ μ΄λνλ 곡κ°.
Garbage Collector(GC)λ μ΄ heap μμμ κ°μ²΄λ€μ μ£ΌκΈ°μ μΌλ‘ κ²μ¬ν΄μ λ μ΄μ μ°Έμ‘°λμ§ μλ κ°μ²΄λ λ©λͺ¨λ¦¬μμ μ κ±°ν©λλ€.
CreatedTue, 01 Apr 2025 13:59:38 +0900 - Young Generation (Young Gen): μλ‘ μμ±λ κ°μ²΄κ° μ μ₯λλ κ³³.
-
@Cacheableμ΄λ Έν μ΄μ μ Spring Frameworkμμ λ©μλμ λ°νκ°μ μΊμμ μ μ₯νμ¬, κ°μ μΈμκ° λ€μ΄μ¬ κ²½μ° μΊμλ κ°μ μ¬μ¬μ©νλλ‘ νλ κΈ°λ₯μ μ 곡ν©λλ€. μ¦, λ©μλλ₯Ό λ°λ³΅μ μΌλ‘ νΈμΆνλλΌλ μ€μ λ‘λ μ²μ ν λ²λ§ μ€νλκ³ , μ΄ν νΈμΆμ μΊμλ κ²°κ³Όλ₯Ό λ°ννκ² λ©λλ€.
π μ£Όμ μν μμ½
- μ€λ³΅ νΈμΆ λ°©μ§: λμΌν μ λ ₯κ°μ λν΄ λ©μλλ₯Ό λ°λ³΅ νΈμΆνλ©΄, μΊμλ κ²°κ³Όλ₯Ό λ°νν΄ μ±λ₯ ν₯μ.
- κ²°κ³Ό μ μ₯: λ©μλ μ€ν κ²°κ³Όλ₯Ό μΊμμ μ μ₯.
- μ‘°κ±΄λΆ μΊμ±: νΉμ 쑰건μ λ°λΌ μΊμ± μ¬λΆ μ μ΄ κ°λ₯.
β κΈ°λ³Έ μ¬μ© μμ
@Cacheable("users") public User findUserById(Long id) { // μ€μ λ‘λ DBλ μΈλΆ API νΈμΆ return userRepository.findById(id).orElse(null); }- μ λ©μλλ
usersλΌλ μΊμ μ΄λ¦μΌλ‘ κ²°κ³Όλ₯Ό μ μ₯ν©λλ€. idκ° λμΌν κ²½μ°, μ΄ν νΈμΆμ μΊμλUserκ°μ²΄λ₯Ό λ°νν©λλ€.
π§ μ£Όμ μμ±
μμ± μ€λͺ valueλλcacheNamesμ¬μ©ν μΊμ μ΄λ¦ μ§μ keyμΊμ ν€λ₯Ό μ§μ (SpEL ννμ μ¬μ© κ°λ₯) conditionμΊμ±ν 쑰건 μ§μ (SpEL) unlessκ²°κ³Όλ₯Ό μ μ₯νμ§ μμ 쑰건 (SpEL) μμ:
@Cacheable(value = "books", key = "#isbn", unless = "#result == null") public Book findBook(String isbn) { return bookRepository.findByIsbn(isbn); }
π§ ν¨κ» μμλλ©΄ μ’μ μ΄λ Έν μ΄μ
@CachePut: μΊμλ₯Ό κ°μ λ‘ μ λ°μ΄νΈν λ μ¬μ©@CacheEvict: μΊμλ₯Ό μμ ν λ μ¬μ©@Caching: μ¬λ¬ μΊμ μ΄λ Έν μ΄μ μ ν¨κ» μ¬μ©ν λ
νμνλ€λ©΄ Redisλ Caffeine κ°μ μΊμ μ€ν μ΄μ ν¨κ» μ°λλ κ°λ₯ν©λλ€.
CreatedTue, 01 Apr 2025 11:34:10 +0900 -
β μλΈλͺ¨λ μμ ν μ΄κΈ°ννκ³ λ€μ μ ν
-
μλΈλͺ¨λμ Gitμμ λ¨Όμ μ κ±°
git submodule deinit -f . -
μλΈλͺ¨λ λλ ν 리λ₯Ό μ κ±°
rm -rf .git/modules/<μλΈλͺ¨λ_μ΄λ¦> rm -rf <μλΈλͺ¨λ_λλ ν 리> -
.gitmodulesνμΌ μμ rm .gitmodules -
μΈλ±μ€μμ μλΈλͺ¨λ νμ μ κ±°
git rm --cached <μλΈλͺ¨λ_λλ ν 리> -
컀λ°
git commit -m "Remove submodule completely" -
μλ‘μ΄ μλΈλͺ¨λ μΆκ° (νμν κ²½μ°)
git submodule add <repo-url> <path> git commit -m "Add submodule"
π§ μ 리νμλ©΄:
- λ¨μν
.gitmodulesνμΌλ§ μ§μ°λ©΄ μ λκ³ , - Git λ΄λΆμ μλΈλͺ¨λ μ 보 (
.git/modules, μΈλ±μ€ λ±)λ κ°μ΄ μ 리ν΄μ€μΌ ν΄μ.
CreatedTue, 01 Apr 2025 11:25:45 +0900 -
-
k8sλ?
**Kubernetes (k8s)**λ 컨ν μ΄λνλ μ ν리μΌμ΄μ μ μλμΌλ‘ λ°°ν¬, νμ₯, κ΄λ¦¬ν΄μ£Όλ μ€νμμ€ μ»¨ν μ΄λ μ€μΌμ€νΈλ μ΄μ νλ«νΌμ λλ€. ꡬκΈμμ κ°λ°ν κΈ°μ μ κΈ°λ°μΌλ‘ CNCF(Cloud Native Computing Foundation)μ κΈ°λΆλμκ³ , νμ¬λ ν΄λΌμ°λ νκ²½μμ κ°μ₯ λ리 μ¬μ©λλ 컨ν μ΄λ κ΄λ¦¬ μμ€ν μ΄μμ.
ν΅μ¬ κ°λ μ 리
- 컨ν μ΄λ(Container): μ ν리μΌμ΄μ κ³Ό κ·Έ μ€ν νκ²½μ ν¨ν€μ§ν λ¨μ (μ£Όλ‘ Docker μ¬μ©)
- Pod: Kubernetesμ κ°μ₯ μμ λ°°ν¬ λ¨μ. νλ μ΄μμ 컨ν μ΄λλ₯Ό ν¬ν¨ν μ μμ΄μ.
- Node: 컨ν μ΄λκ° μ€μ λ‘ μ€νλλ 물리/κ°μ λ¨Έμ
- Cluster: μ¬λ¬ λ Έλλ‘ κ΅¬μ±λ Kubernetesμ μ 체 μΈνλΌ
- Deployment: μ ν리μΌμ΄μ μ μνλ μν(μ: λͺ κ°μ 볡μ λ³Έ μ μ§ λ±)λ₯Ό μ μνκ³ κ΄λ¦¬
- Service: Podμ μ κ·Όν μ μλ μμ μ μΈ λ€νΈμν¬ μ£Όμλ₯Ό μ 곡 (LoadBalancer, ClusterIP, NodePort λ±)
- Ingress: μΈλΆ νΈλν½μ λ΄λΆ μλΉμ€λ‘ λΌμ°ν ν΄μ£Όλ HTTP(S) λ 벨 λΌμ°ν°
- ConfigMap / Secret: μ ν리μΌμ΄μ μ€μ κ°μ΄λ λ―Όκ°ν μ 보λ₯Ό μΈλΆμμ μ£Όμ
Kubernetesμ μ₯μ
- μλνλ λ°°ν¬ λ° λ‘€λ°±: μ ν리μΌμ΄μ μ μμ½κ² λ°°ν¬/μ λ°μ΄νΈνκ³ , λ¬Έμ λ°μ μ μλμΌλ‘ μ΄μ λ²μ μΌλ‘ λ‘€λ°± κ°λ₯
- μλ μ€μΌμΌλ§: νΈλν½μ λ°λΌ Pod μλ₯Ό μλ μ‘°μ κ°λ₯
- μκ° μΉμ (Self-healing): μ₯μ λ 컨ν μ΄λλ μλμΌλ‘ λ€μ μμ±
- μλΉμ€ λμ€μ»€λ²λ¦¬ λ° λ‘λ λ°Έλ°μ±: Pod κ° ν΅μ μ μ½κ² ν μ μκ² λμμ€
κ°λ¨ν λμ νλ¦
- μ¬μ©μλ μ ν리μΌμ΄μ μ 컨ν μ΄λ(Docker)λ‘ ν¨ν€μ§
- μ΄λ₯Ό Kubernetes ν΄λ¬μ€ν°μ λ°°ν¬
- Kubernetesκ° μ΄λ₯Ό μ μ ν Nodeμ μ€μΌμ€λ§νκ³ μ€ν
- μλΉμ€λ Ingressλ₯Ό ν΅ν΄ μΈλΆ μ κ·Ό κ°λ₯
helm μ΄λ?
Helmμ Kubernetesμ ν¨ν€μ§ κ΄λ¦¬μμ λλ€. μ½κ² λ§ν΄μ, Kubernetesμμ μ ν리μΌμ΄μ μ λ μ½κ³ , μ¬μ¬μ© κ°λ₯νκ² λ°°ν¬ν μ μλλ‘ λμμ£Όλ λꡬμ λλ€. λ§μΉ
apt(Ubuntu),brew(macOS),npm(Node.js) κ°μ λλ.CreatedMon, 31 Mar 2025 12:48:01 +0900 -
PreFlightCorsConfig example
@Configuration public class PreFlightCorsConfig { private final CorsProperties corsProperties; public PreFlightCorsConfig(CorsProperties corsProperties) { this.corsProperties = corsProperties; } private static final String ALLOWED_HEADERS = "x-requested-with,authorization, refresh-token,Access-Control-Allow-Origin,Content-Type, credential,X-AUTH-TOKEN,X-CSRF-TOKEN"; private static final String ALLOWED_METHODS = "GET, PUT, POST, PATCH, DELETE, OPTIONS"; private static final String ALLOWED_CREDENTIALS = "true"; // μ€μ ν΄λΉ ν€λκ° μλ€λ©΄ axiosλ₯Ό ν΅ν νλ‘ νΈμμ νμΈμ΄ λΆκ°λ₯ν¨ private static final String EXPOSE_HEADERS = "*,Authorization,Refresh-token,authorization,refresh-token"; private static final String MAX_AGE = "3600"; @Bean public WebFilter corsFilter() { return (ServerWebExchange ctx, WebFilterChain chain) -> { ServerHttpRequest request = ctx.getRequest(); String requestOrigin = request.getHeaders().getOrigin(); if (CorsUtils.isPreFlightRequest(request)) { ServerHttpResponse response = ctx.getResponse(); HttpHeaders headers = response.getHeaders(); if (requestOrigin != null && corsProperties.getAllowedOrigins().contains(requestOrigin)) { headers.add("Access-Control-Allow-Origin", requestOrigin); } headers.add("Access-Control-Allow-Methods", ALLOWED_METHODS); headers.add("Access-Control-Max-Age", MAX_AGE); headers.add("Access-Control-Allow-Headers", ALLOWED_HEADERS); headers.add("Access-Control-Allow-Credentials", ALLOWED_CREDENTIALS); headers.add("Access-Control-Expose-Headers", EXPOSE_HEADERS); // νλ‘ νΈμμ ν΄λΉ ν€λλ₯Ό νμΈν μμκ² νκ°ν΄μ€ headers.add("Access-Control-Expose-Headers", EXPOSE_HEADERS); if (request.getMethod() == HttpMethod.OPTIONS) { response.setStatusCode(HttpStatus.OK); return Mono.empty(); } } return chain.filter(ctx); }; } }CorsProperties
@Component @Getter @Setter @ConfigurationProperties(prefix = "app.cors") public class CorsProperties { private List<String> allowedOrigins = new ArrayList<>(); }μ€μ
- dev, prod profile λ³λ‘ λ€λ₯΄κ² μ€μ νλ κ² μΌλ°μ
# application-dev.yml cors: allowed-origins: - "http://localhost:3000" - "http://127.0.0.1:3000"# application-prod.yml cors: allowed-origins: - "https://example.com" - "https://another-domain.com"spspring-cloud-starter-gateway μ¬μ©μ
Spring Cloud Gatewayλ₯Ό μ¬μ©νκ³ μλ€λ©΄
PreFlightCorsConfigκ°μ λ³λμ Java Config μμ΄λapplication.ymlμ μ€μ λ§μΌλ‘ CORS μ²λ¦¬κ° κ°λ₯ν©λλ€.**CreatedMon, 31 Mar 2025 00:49:17 +0900