-
Ubuntuμμ PostgreSQLμ μ€μΉνκ³ μΈλΆ μ μμ νμ©νλ €λ©΄ λ€μκ³Ό κ°μ λ¨κ³λ‘ μ§ννλ©΄ λ©λλ€.
1. PostgreSQL μ€μΉ
sudo apt update sudo apt install postgresql postgresql-contribμ€μΉ ν PostgreSQL μλΉμ€κ° μλ μμλ©λλ€. μν νμΈ:
sudo systemctl status postgresql
2. PostgreSQL μ μ λ° DB μ€μ
PostgreSQL μ€μΉ μ κΈ°λ³Έμ μΌλ‘
postgresλΌλ μ μ κ° μμ±λ©λλ€.sudo -i -u postgres psqlμνλ μ μ μ DBλ₯Ό λ§λ€κ³ λΉλ°λ²νΈ μ€μ :
CREATE USER myuser WITH PASSWORD 'mypassword'; CREATE DATABASE mydb OWNER myuser;κΆν λΆμ¬:
CreatedMon, 23 Jun 2025 10:14:00 +0900 -
컀λ§(Currying)μ μ¬λ¬ κ°μ μΈμλ₯Ό λ°λ ν¨μλ₯Ό νλμ μΈμλ§ λ°λ ν¨μλ€μ μ°μμΌλ‘ λ³ννλ κΈ°λ²μ λλ€. μνμ νμ€μΌ 컀리(Haskell Curry)μ μ΄λ¦μμ λ°μ¨ μ©μ΄μ λλ€.
κΈ°λ³Έ κ°λ
μΌλ°μ μΈ ν¨μ:
function add(a, b, c) { return a + b + c; } add(1, 2, 3); // 6컀λ§λ ν¨μ:
function curriedAdd(a) { return function(b) { return function(c) { return a + b + c; }; }; } curriedAdd(1)(2)(3); // 6νμ΄ν ν¨μλ‘ κ°λ¨νκ²
const curriedAdd = a => b => c => a + b + c; const result = curriedAdd(1)(2)(3); // 6λΆλΆ μ μ© (Partial Application)
컀λ§μ κ°μ₯ ν° μ₯μ μ λΆλΆ μ μ©μ΄ κ°λ₯νλ€λ κ²μ λλ€:
const curriedAdd = a => b => c => a + b + c; // λΆλΆ μ μ© const add5 = curriedAdd(5); const add5And3 = add5(3); console.log(add5And3(2)); // 10 (5 + 3 + 2) console.log(add5(4)(1)); // 10 (5 + 4 + 1)μ€μ©μ μΈ μμ λ€
1. λ¬Έμμ΄ κ²μ¬ ν¨μ
const hasSubstring = substring => str => str.includes(substring); const hasReact = hasSubstring('React'); const hasVue = hasSubstring('Vue'); console.log(hasReact('React κ°λ°μ')); // true console.log(hasVue('Vue.js νλ μμν¬')); // true // λ°°μ΄μμ νμ© const frameworks = ['React', 'Angular', 'Vue', 'Svelte']; const reactItems = frameworks.filter(hasReact); console.log(reactItems); // ['React']2. μν μ°μ°
const multiply = a => b => a * b; const divide = a => b => a / b; const double = multiply(2); const triple = multiply(3); const half = divide(1/2); // λλ multiply(0.5) console.log(double(5)); // 10 console.log(triple(4)); // 12 const numbers = [1, 2, 3, 4, 5]; const doubled = numbers.map(double); console.log(doubled); // [2, 4, 6, 8, 10]3. μ΄λ²€νΈ νΈλ€λ¬
const log = level => message => { console.log(`[${level}] ${message}`); }; const logError = log('ERROR'); const logInfo = log('INFO'); logError('λ°μ΄ν°λ² μ΄μ€ μ°κ²° μ€ν¨'); // [ERROR] λ°μ΄ν°λ² μ΄μ€ μ°κ²° μ€ν¨ logInfo('μλ² μμλ¨'); // [INFO] μλ² μμλ¨4. API μμ²
const apiRequest = method => url => data => { return fetch(url, { method: method, headers: { 'Content-Type': 'application/json' }, body: data ? JSON.stringify(data) : undefined }); }; const get = apiRequest('GET'); const post = apiRequest('POST'); const put = apiRequest('PUT'); // μ¬μ© get('/api/users')(); post('/api/users')({ name: 'κΉμ² μ', email: 'kim@example.com' });μλ μ»€λ§ ν¨μ λ§λ€κΈ°
function curry(fn) { return function curried(...args) { if (args.length >= fn.length) { return fn.apply(this, args); } else { return function(...nextArgs) { return curried.apply(this, args.concat(nextArgs)); }; } }; } // μ¬μ© μ function add(a, b, c) { return a + b + c; } const curriedAdd = curry(add); console.log(curriedAdd(1)(2)(3)); // 6 console.log(curriedAdd(1, 2)(3)); // 6 console.log(curriedAdd(1)(2, 3)); // 6 console.log(curriedAdd(1, 2, 3)); // 6λΌμ΄λΈλ¬λ¦¬μμμ νμ©
Lodash
const _ = require('lodash'); const add = (a, b, c) => a + b + c; const curriedAdd = _.curry(add); console.log(curriedAdd(1)(2)(3)); // 6Ramda
const R = require('ramda'); const users = [ { name: 'κΉμ² μ', age: 25 }, { name: 'μ΄μν¬', age: 30 }, { name: 'λ°λ―Όμ', age: 35 } ]; const getProperty = R.curry((prop, obj) => obj[prop]); const getName = getProperty('name'); const names = users.map(getName); console.log(names); // ['κΉμ² μ', 'μ΄μν¬', 'λ°λ―Όμ']컀λ§μ μ₯μ
- μ¬μ¬μ©μ±: νΉμ λ§€κ°λ³μκ° κ³ μ λ μλ‘μ΄ ν¨μλ₯Ό μ½κ² μμ±
- κ°λ μ±: ν¨μμ μλκ° λ λͺ νν΄μ§
- ν¨μ μ‘°ν©: λ€λ₯Έ κ³ μ°¨ ν¨μλ€κ³Ό μ‘°ν©νκΈ° μ¬μ
- λΆλΆ μ μ©: νμν λ μΌλΆ μΈμλ§ λ¨Όμ μ μ© κ°λ₯
μ£Όμμ¬ν
- μ±λ₯: ν¨μ νΈμΆμ΄ μ€μ²©λλ―λ‘ μ½κ°μ μ€λ²ν€λ λ°μ
- λλ²κΉ : μ€ν νΈλ μ΄μ€κ° 볡μ‘ν΄μ§ μ μμ
- κ°λ μ±: κ³Όλνκ² μ¬μ©νλ©΄ μ€νλ € μ½λλ₯Ό μ΄ν΄νκΈ° μ΄λ €μμ§ μ μμ
컀λ§μ ν¨μν νλ‘κ·Έλλ°μ ν΅μ¬ κ°λ μ€ νλλ‘, μ½λμ μ¬μ¬μ©μ±κ³Ό λͺ¨λμ±μ ν¬κ² ν₯μμν¬ μ μλ κ°λ ₯ν κΈ°λ²μ λλ€.
CreatedFri, 20 Jun 2025 14:11:00 +0900 -
1. ν¨μν μΈν°νμ΄μ€ μ¬μ© (Java 8+)
κΈ°λ³Έμ μΈ μ»€λ§ κ΅¬ν
import java.util.function.Function; public class CurryingExample { // 3κ° μΈμλ₯Ό λ°λ ν¨μλ₯Ό μ»€λ§ public static Function<Integer, Function<Integer, Function<Integer, Integer>>> curriedAdd() { return a -> b -> c -> a + b + c; } public static void main(String[] args) { // μ¬μ© μ Function<Integer, Function<Integer, Function<Integer, Integer>>> add = curriedAdd(); int result1 = add.apply(1).apply(2).apply(3); // 6 System.out.println(result1); // λΆλΆ μ μ© Function<Integer, Function<Integer, Integer>> add5 = add.apply(5); Function<Integer, Integer> add5And3 = add5.apply(3); int result2 = add5And3.apply(2); // 10 System.out.println(result2); } }λ 볡μ‘ν μμ - λ¬Έμμ΄ μ²λ¦¬
import java.util.function.Function; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; public class StringCurrying { // λ¬Έμμ΄ ν¬ν¨ κ²μ¬ ν¨μ public static Function<String, Function<String, Boolean>> contains() { return substring -> str -> str.contains(substring); } // λ¬Έμμ΄ λ³ν ν¨μ public static Function<String, Function<String, String>> replace() { return target -> replacement -> str -> str.replace(target, replacement); } public static void main(String[] args) { List<String> frameworks = Arrays.asList("React", "Angular", "Vue", "Svelte"); // λΆλΆ μ μ©λ ν¨μ μμ± Function<String, Boolean> hasReact = contains().apply("React"); // μ€νΈλ¦Όκ³Ό ν¨κ» μ¬μ© List<String> reactItems = frameworks.stream() .filter(hasReact::apply) .collect(Collectors.toList()); System.out.println(reactItems); // [React] // λ¬Έμμ΄ μΉν Function<String, String> replaceJavaWithKotlin = replace().apply("Java").apply("Kotlin"); String result = replaceJavaWithKotlin.apply("I love Java programming"); System.out.println(result); // I love Kotlin programming } }2. μ λ€λ¦μ νμ©ν λ²μ© μ»€λ§ μ νΈλ¦¬ν°
import java.util.function.*; public class CurryUtil { // 2κ° μΈμ ν¨μ μ»€λ§ public static <A, B, R> Function<A, Function<B, R>> curry(BiFunction<A, B, R> function) { return a -> b -> function.apply(a, b); } // 3κ° μΈμ ν¨μλ₯Ό μν ν¨μν μΈν°νμ΄μ€ @FunctionalInterface public interface TriFunction<A, B, C, R> { R apply(A a, B b, C c); } // 3κ° μΈμ ν¨μ μ»€λ§ public static <A, B, C, R> Function<A, Function<B, Function<C, R>>> curry(TriFunction<A, B, C, R> function) { return a -> b -> c -> function.apply(a, b, c); } public static void main(String[] args) { // BiFunction μ»€λ§ BiFunction<Integer, Integer, Integer> multiply = (a, b) -> a * b; Function<Integer, Function<Integer, Integer>> curriedMultiply = curry(multiply); Function<Integer, Integer> double_ = curriedMultiply.apply(2); System.out.println(double_.apply(5)); // 10 // TriFunction μ»€λ§ TriFunction<Integer, Integer, Integer, Integer> add3 = (a, b, c) -> a + b + c; Function<Integer, Function<Integer, Function<Integer, Integer>>> curriedAdd3 = curry(add3); int result = curriedAdd3.apply(1).apply(2).apply(3); System.out.println(result); // 6 } }3. ν΄λμ€ κΈ°λ° κ΅¬ν
public class MathOperations { public static class CurriedAdd { private final int first; private CurriedAdd(int first) { this.first = first; } public static CurriedAdd of(int first) { return new CurriedAdd(first); } public CurriedAddSecond with(int second) { return new CurriedAddSecond(first, second); } public class CurriedAddSecond { private final int second; private CurriedAddSecond(int first, int second) { this.second = second; } public int and(int third) { return first + second + third; } } } public static void main(String[] args) { int result = CurriedAdd.of(1).with(2).and(3); // 6 System.out.println(result); // λΆλΆ μ μ© CurriedAdd add5 = CurriedAdd.of(5); CurriedAdd.CurriedAddSecond add5And3 = add5.with(3); int result2 = add5And3.and(2); // 10 System.out.println(result2); } }4. μ€μ©μ μΈ μμ - HTTP ν΄λΌμ΄μΈνΈ
import java.util.function.Function; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.net.URI; public class CurriedHttpClient { public static Function<String, Function<String, Function<String, HttpRequest>>> httpRequest() { return method -> url -> body -> { HttpRequest.Builder builder = HttpRequest.newBuilder() .uri(URI.create(url)); switch (method.toUpperCase()) { case "GET": builder.GET(); break; case "POST": builder.POST(HttpRequest.BodyPublishers.ofString(body)); break; case "PUT": builder.PUT(HttpRequest.BodyPublishers.ofString(body)); break; } return builder.build(); }; } public static void main(String[] args) { // λΆλΆ μ μ©λ ν¨μλ€ μμ± Function<String, Function<String, HttpRequest>> get = httpRequest().apply("GET"); Function<String, Function<String, HttpRequest>> post = httpRequest().apply("POST"); // μ¬μ© HttpRequest getRequest = get.apply("https://api.example.com/users").apply(""); HttpRequest postRequest = post.apply("https://api.example.com/users") .apply("{\"name\":\"κΉμ² μ\"}"); System.out.println("GET: " + getRequest.uri()); System.out.println("POST: " + postRequest.uri()); } }5. Vavr λΌμ΄λΈλ¬λ¦¬ μ¬μ©
Vavr(μ΄μ Javaslang)λ μλ°μμ ν¨μν νλ‘κ·Έλλ°μ λ μ½κ² ν μ μκ² ν΄μ£Όλ λΌμ΄λΈλ¬λ¦¬μ λλ€:
CreatedFri, 20 Jun 2025 14:11:00 +0900 -
JavaScriptμμ **ν΄λ‘μ (Closure)**λ ν¨μμ κ·Έ ν¨μκ° μ μΈλ μ΄νμ νκ²½(Lexical Environment)μ μ‘°ν©μ λ§ν©λλ€. μ¦, ν¨μκ° μΈλΆ μ€μ½νμ λ³μμ μ κ·Όν μ μλ κΈ°λ₯μ μλ―Έν©λλ€.
π κΈ°λ³Έ κ°λ
ν΄λ‘μ λ λ€μκ³Ό κ°μ 쑰건μμ μμ±λ©λλ€:
- ν¨μ μμ ν¨μκ° μ μλμ΄ μκ³ ,
- λ΄λΆ ν¨μκ° μΈλΆ ν¨μμ λ³μμ μ κ·Όν μ μμΌλ©°,
- μΈλΆ ν¨μμ μ€νμ΄ λλ λ€μλ λ΄λΆ ν¨μκ° μ¬μ ν κ·Έ λ³μμ μ κ·Όν μ μμ λ ν΄λ‘μ κ° λ©λλ€.
function outer() { let count = 0; return function inner() { count++; console.log(count); } } const counter = outer(); counter(); // 1 counter(); // 2μ μμ μμ
inner()ν¨μλouter()μ μ§μ λ³μcountμ μ κ·Όν©λλ€.outer()μ μ€νμ΄ λλ¬μμλcounter()κ°countμ κ³μ μ κ·Όν μ μλ μ΄μ λ ν΄λ‘μ λλ¬Έμ λλ€.CreatedFri, 20 Jun 2025 14:06:17 +0900 -
MySQLμ λΉλ‘―ν λλΆλΆμ κ΄κ³ν λ°μ΄ν°λ² μ΄μ€μμ μ¬μ©λλ JOINμ μ¬λ¬ ν μ΄λΈμ λ°μ΄ν°λ₯Ό μ‘°ν©ν΄ νλμ κ²°κ³Όλ‘ λ§λλ λ° μ¬μ©λ©λλ€. μλλ λνμ μΈ JOIN μ’ λ₯μ κ°κ°μ μ€λͺ , μμ λ₯Ό ν¬ν¨ν μ 리μ λλ€.
π 1. INNER JOIN
β μ μ
λ ν μ΄λΈμμ 곡ν΅λ κ°μ΄ μ‘΄μ¬νλ νλ§ λ°νν©λλ€.
π§ͺ μμ
SELECT a.id, a.name, b.order_date FROM customers a INNER JOIN orders b ON a.id = b.customer_id;π νΉμ§
- κ°μ₯ μΌλ°μ μΈ JOIN λ°©μ
- μμͺ½ λͺ¨λ μΌμΉνλ κ°μ΄ μμ΄μΌ κ²°κ³Όμ ν¬ν¨λ¨
π 2. LEFT JOIN (λλ LEFT OUTER JOIN)
β μ μ
μΌμͺ½ ν μ΄λΈμ λͺ¨λ νμ λ°ννλ©°, μ€λ₯Έμͺ½ ν μ΄λΈμ μΌμΉνλ κ°μ΄ μμΌλ©΄
NULLλ‘ μ±μμ§λλ€.CreatedWed, 18 Jun 2025 11:27:58 +0900 -
HttpApiCachingFilter μ CachedHttpServletRequestWrapper
- jsonκ³Ό multipart μΈ form-urlencodedμ κ²½μ°λ νμ©νλ©΄μ logging μ 보 λ¨κΈ°λ filter ꡬν μμ
- request μ inputStreamμ νλ² μ½μΌλ©΄ νλ°λκΈ° λλ¬Έμ μ΄λ₯Ό λ°©μ§νλ λ‘μ§μ΄ μ€μν©λλ€.
HttpApiCachingFilter
@Component @Order(value = Ordered.HIGHEST_PRECEDENCE) @WebFilter(filterName = "HttpApiCachingFilter", urlPatterns = "/*") @Slf4j public class HttpApiCachingFilter extends OncePerRequestFilter { private static String APPLICATION_NAME; @Value("${spring.application.name}") private void setApplicationName(String value) { APPLICATION_NAME = value; } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { if (isAsyncDispatch(request)) { filterChain.doFilter(request, response); } else if (isMultipartRequest(request)) { doFilterWrapped(new StandardMultipartHttpServletRequest(request), new CachedHttpServletResponseWrapper(response), filterChain); } else { doFilterWrapped(new CachedHttpServletRequestWrapper(request), new CachedHttpServletResponseWrapper(response), filterChain); } } protected void doFilterWrapped(HttpServletRequestWrapper request, ContentCachingResponseWrapper response, FilterChain filterChain) throws ServletException, IOException { try { logRequestHeader(request); logRequest(request); filterChain.doFilter(request, response); } finally { logResponseHeader(response); logResponse(response); response.copyBodyToResponse(); } } private static void logRequestHeader(HttpServletRequestWrapper request) { Enumeration<String> headerNames = request.getHeaderNames(); if (headerNames != null) { Collections.list(headerNames).forEach(headersName -> { String headerValue = request.getHeader(headersName); log.info("Request Header {}: {}", headersName, headerValue); }); } } private static void logRequest(HttpServletRequestWrapper request) throws IOException { String contentType = request.getContentType(); log.info("Request body: {} uri=[{}] content-type=[{}]", request.getMethod(), request.getRequestURI(), contentType); if (contentType != null && contentType.startsWith(MediaType.APPLICATION_FORM_URLENCODED_VALUE)) { Map<String, String[]> paramMap = request.getParameterMap(); paramMap.forEach((key, values) -> { for (String value : values) { log.info("REQUEST Param: {} = {}", key, value); } }); } else { logPayload("REQUEST", contentType, request.getInputStream(), request.getRequestURI()); } } private static void logResponseHeader(ContentCachingResponseWrapper response) { Collection<String> headerNames = response.getHeaderNames(); for (String headerName : headerNames) { for (String value : response.getHeaders(headerName)) { log.info("Response Header {}: {}", headerName, value); } } } private static void logResponse(ContentCachingResponseWrapper response) throws IOException { logPayload("RESPONSE", response.getContentType(), response.getContentInputStream(), null); } private static void logPayload(String direction, String contentType, InputStream inputStream, String targetUri ) throws IOException { boolean visible = isVisible(MediaType.valueOf(contentType == null ? "application/json" : contentType)); if (visible) { byte[] content = StreamUtils.copyToByteArray(inputStream); if (content.length > 0) { String contentString = new String(content); log.info("{} Payload: {}", direction, contentString); } } else { log.info("{} Payload: Binary Content", direction); } } private static boolean isVisible(MediaType mediaType) { final List<MediaType> VISIBLE_TYPES = Arrays.asList(MediaType.valueOf("text/*"), MediaType.APPLICATION_FORM_URLENCODED, MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.valueOf("application/*+json"), MediaType.valueOf("application/*+xml"), MediaType.MULTIPART_FORM_DATA); return VISIBLE_TYPES.stream().anyMatch(visibleType -> visibleType.includes(mediaType)); } private boolean isMultipartRequest(HttpServletRequest request) { String contentType = request.getContentType(); return request.getMethod().equalsIgnoreCase("POST") && contentType != null && contentType.startsWith("multipart/form-data"); } }CachedHttpServletRequestWrapper.java
public class CachedHttpServletRequestWrapper extends HttpServletRequestWrapper { private final byte[] cachedBody; private Map<String, String[]> parameterMap; public CachedHttpServletRequestWrapper(HttpServletRequest request) throws IOException { super(request); this.cachedBody = request.getInputStream().readAllBytes(); // form-urlencoded νμ μΌ λλ§ νλΌλ―Έν° νμ± String contentType = request.getContentType(); if (contentType != null && contentType.startsWith(MediaType.APPLICATION_FORM_URLENCODED_VALUE)) { String encoding = request.getCharacterEncoding(); if (encoding == null) { encoding = StandardCharsets.UTF_8.name(); } this.parameterMap = parseFormParameters(new String(this.cachedBody, encoding)); } else { // μλ³Έ requestμ νλΌλ―Έν° λ§΅ μ¬μ© this.parameterMap = super.getParameterMap(); } } @Override public ServletInputStream getInputStream() { ByteArrayInputStream bais = new ByteArrayInputStream(cachedBody); return new ServletInputStream() { @Override public int read() { return bais.read(); } @Override public boolean isFinished() { return bais.available() == 0; } @Override public boolean isReady() { return true; } @Override public void setReadListener(ReadListener listener) { } }; } @Override public Map<String, String[]> getParameterMap() { return this.parameterMap; } @Override public BufferedReader getReader() { String encoding = getCharacterEncoding(); if (encoding == null) { encoding = StandardCharsets.UTF_8.name(); } return new BufferedReader(new InputStreamReader(getInputStream(), Charset.forName(encoding))); } private Map<String, String[]> parseFormParameters(String body) throws UnsupportedEncodingException { Map<String, List<String>> tempMap = new LinkedHashMap<>(); // λΉ λ°λ μ²λ¦¬ if (body == null || body.trim().isEmpty()) { return new LinkedHashMap<>(); } for (String pair : body.split("&")) { if (pair.trim().isEmpty()) { continue; // λΉ pair 건λλ°κΈ° } String[] parts = pair.split("=", 2); if (parts.length == 0) { continue; } String key = URLDecoder.decode(parts[0], StandardCharsets.UTF_8); String value = parts.length > 1 ? URLDecoder.decode(parts[1], StandardCharsets.UTF_8) : ""; tempMap.computeIfAbsent(key, k -> new ArrayList<>()).add(value); } return tempMap.entrySet().stream() .collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().toArray(new String[0]))); } }CachedHttpServletResponseWrapper.java
public class CachedHttpServletResponseWrapper extends ContentCachingResponseWrapper { public CachedHttpServletResponseWrapper(HttpServletResponse response) { super(response); } }CreatedMon, 16 Jun 2025 20:49:58 +0900