-
โ ์๋ธ๋ชจ๋ ์์ ํ ์ด๊ธฐํํ๊ณ ๋ค์ ์ ํ
-
์๋ธ๋ชจ๋์ 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 -
Spring์ ํฌํจํ Java ๊ธฐ๋ฐ ์ ํ๋ฆฌ์ผ์ด์ ์์ **VO(Value Object)**์ **DTO(Data Transfer Object)**๋ ๊ฐ๋ ์ ์ผ๋ก ๋น์ทํด ๋ณด์ผ ์ ์์ง๋ง, ๊ทธ ๋ชฉ์ ๊ณผ ์ฌ์ฉํ๋ ๋งฅ๋ฝ์์ ์ฐจ์ด๊ฐ ์์ต๋๋ค. ์๋์ ๊ฐ๊ฐ์ ์ค๋ช ํ๊ณ ์ฐจ์ด๋ฅผ ์ ๋ฆฌํ ๊ฒ์.
1. VO (Value Object)
- ์๋ฏธ: ๊ฐ ์์ฒด๋ฅผ ํํํ๋ ๊ฐ์ฒด. ๊ฐ์ ๋๋ฑ์ฑ์ ๋น๊ตํ ๋, ๊ฐ์ฒด์ ์ฐธ์กฐ๊ฐ ์๋๋ผ ๊ฐ์ด ๊ฐ์์ง๋ก ํ๋จํฉ๋๋ค.
- ํน์ง:
- **๋ถ๋ณ(Immutable)**ํ๊ฒ ์ค๊ณํ๋ ๊ฒ์ด ์ผ๋ฐ์ ์ ๋๋ค.
equals(),hashCode()๋ฅผ ์ฌ์ ์ํด์ ๊ฐ์ด ๊ฐ์ผ๋ฉด ๋์ผํ ๊ฐ์ฒด๋ก ๊ฐ์ฃผํฉ๋๋ค.- ์ฃผ๋ก ๋๋ฉ์ธ ๋ชจ๋ธ์์ ์ฌ์ฉ๋๋ฉฐ, ํน์ ๊ฐ๋ (์: ์ฃผ์, ๋, ์ด๋ฆ ๋ฑ)์ ์บก์ํํฉ๋๋ค.
public class Address { private final String city; private final String street; public Address(String city, String street) { this.city = city; this.street = street; } // equals, hashCode ๋ฐ๋์ ์ฌ์ ์ }
2. DTO (Data Transfer Object)
- ์๋ฏธ: ๊ณ์ธต ๊ฐ ๋ฐ์ดํฐ ์ ๋ฌ์ ์ํด ์ฌ์ฉํ๋ ๊ฐ์ฒด (์: Controller โ Service โ Repository)
- ํน์ง:
- ๋ถ๋ณ์ผ ํ์ ์์, Getter/Setter ์์
- ๋จ์ํ ๋ฐ์ดํฐ๋ฅผ ์ฎ๊ธฐ๋ ๋ชฉ์ (๋ก์ง์ด ์์ด์ผ ํจ)
- ๋ณดํต ์ ๋ ฅ(Request), **์ถ๋ ฅ(Response)**์ฉ์ผ๋ก ๋ง์ด ์ฐ์
public class UserResponseDto { private String name; private String email; // ๊ธฐ๋ณธ ์์ฑ์, getter/setter }
์ ๋ฆฌ: VO vs DTO
ํญ๋ชฉ VO (Value Object) DTO (Data Transfer Object) ๋ชฉ์ ๊ฐ ์์ฒด๋ฅผ ํํ ๋ฐ์ดํฐ ์ ๋ฌ ๋ถ๋ณ์ฑ ๋ณดํต ๋ถ๋ณ ๋ณดํต ๊ฐ๋ณ ์ฌ์ฉ ์์น ๋๋ฉ์ธ ๋ชจ๋ธ ๊ณ์ธต ๊ฐ ๋ฐ์ดํฐ ์ ๋ฌ equals/hashCode ๊ฐ ๊ธฐ์ค์ผ๋ก ์ฌ์ ์ ์ผ๋ฐ์ ์ผ๋ก ์ฌ์ ์ํ์ง ์์ ๋ก์ง ํฌํจ ๊ฐ๋ฅ ์์ (์์ ๋ฐ์ดํฐ๋ง)
์ฌ์ฉ ์์
๊ฐ๋จํ Spring ๊ธฐ๋ฐ ์ ํ๋ฆฌ์ผ์ด์ ๊ตฌ์กฐ์์ VO์ DTO์ ์ฐจ์ด๋ฅผ ์ค์ ์์ ์ฝ๋์ ๋๋ค. ์์๋ โ์ฌ์ฉ์(User) ์ ๋ณด ์ฒ๋ฆฌโ๋ฅผ ์ค์ฌ์ผ๋ก ๊ตฌ์ฑํฉ๋๋ค.
CreatedSun, 30 Mar 2025 17:14:05 +0900 -
JPA์ฟผ๋ฆฌ๋ฅผ ๋ก๊ทธ๋ก ์ถ๋ ฅํ๊ธฐ ์ํ yml ์ค์
Spring Boot์์ JPA ์ฟผ๋ฆฌ๋ฅผ ๋ก๊ทธ๋ก ์ถ๋ ฅํ๊ณ ์ถ๋ค๋ฉด
application.ymlํ์ผ์ ๋ค์๊ณผ ๊ฐ์ ์ค์ ์ ์ถ๊ฐํ๋ฉด ๋ฉ๋๋ค:spring: jpa: show-sql: true # SQL ์ฟผ๋ฆฌ๋ฅผ ์ฝ์์ ์ถ๋ ฅ properties: hibernate: format_sql: true # SQL ์ฟผ๋ฆฌ๋ฅผ ๋ณด๊ธฐ ์ข๊ฒ ํฌ๋งท logging: level: org.hibernate.SQL: debug # ์คํ๋๋ SQL ๋ก๊ทธ org.hibernate.type.descriptor.sql.BasicBinder: trace # ๋ฐ์ธ๋ฉ๋๋ ํ๋ผ๋ฏธํฐ ๊ฐ ๋ก๊ทธ์ค๋ช :
show-sql: Hibernate๊ฐ ์คํํ๋ SQL์ ํ์ค ์ถ๋ ฅ์ผ๋ก ๋ณด์ฌ์ค (๊ฐ๋จํ ๋ก๊ทธ).format_sql: SQL ์ฟผ๋ฆฌ๋ฅผ ์ฝ๊ธฐ ์ฝ๊ฒ ์ค๋ฐ๊ฟ ๋ฐ ๋ค์ฌ์ฐ๊ธฐ.logging.level.org.hibernate.SQL: ๋ ์์ธํ SQL ๋ก๊ทธ (๋ก๊น ํ๋ ์์ํฌ ํตํด ์ถ๋ ฅ).logging.level.org.hibernate.type.descriptor.sql.BasicBinder: SQL์ ๋ฐ์ธ๋ฉ๋๋ ํ๋ผ๋ฏธํฐ๊น์ง ์ถ๋ ฅํ๊ณ ์ถ์ ๋ ์ฌ์ฉ.
๐ก ํ๋ผ๋ฏธํฐ ๋ก๊ทธ๋ ๋ฏผ๊ฐ ์ ๋ณด๊ฐ ๋ ธ์ถ๋ ์ ์์ผ๋ ์ด์ ํ๊ฒฝ์์๋ ์ฃผ์ํด์ ์ฌ์ฉํ์ธ์.
CreatedFri, 28 Mar 2025 13:25:03 +0900 -
Spring Cloud Gateway์์ globalcors ์ค์
- ํน์ URI ํจํด์ ๋ํด์๋ง CORS๋ฅผ ํ์ฉํ๊ณ ์ถ๋ค๋ฉด, ์ค์ ํ cors-configurations ํค์ ํจํด์ ์ธ๋ถํํ๋ฉด ๋ฉ๋๋ค.
- ํ์ฌ๋ ‘/**‘๋ก ๋ชจ๋ ๊ฒฝ๋ก์ ๋ํด ํ์ฉํ๊ณ ์์ผ๋ฏ๋ก, ์ด๋ฅผ ์ ํํ๋ ค๋ฉด ์๋ฅผ ๋ค์ด /api/public/** ์ฒ๋ผ ํน์ ๊ฒฝ๋ก๋ก ์ขํ์ฃผ๋ฉด ๋ฉ๋๋ค.
Spring Cloud Gateway์์
globalcors์ค์ ์ ์ฌ์ฉํ ๋ ํน์ URI ํจํด์ ๋ํด์๋ง CORS๋ฅผ ํ์ฉํ๊ณ ์ถ๋ค๋ฉด, ์ค์ ํcors-configurationsํค์ ํจํด์ ์ธ๋ถํํ๋ฉด ๋ฉ๋๋ค. ํ์ฌ๋'/\*\*'๋ก ๋ชจ๋ ๊ฒฝ๋ก์ ๋ํด ํ์ฉํ๊ณ ์์ผ๋ฏ๋ก, ์ด๋ฅผ ์ ํํ๋ ค๋ฉด ์๋ฅผ ๋ค์ด/api/public/**์ฒ๋ผ ํน์ ๊ฒฝ๋ก๋ก ์ขํ์ฃผ๋ฉด ๋ฉ๋๋ค.CreatedFri, 28 Mar 2025 11:41:41 +0900