분류 전체보기 검색 결과

178개 발견
  1. 미리보기
    2020.10.05 - mr.november11

    [nginx] nginx 페이지 응답 헤더에 버전 정보가 노출될 경우 조치 방법

  2. 미리보기
    2020.07.08 - mr.november11

    스프링 부트에서 도커 컨테이너 이미지 생성하기

  3. 미리보기
    2020.07.02 - mr.november11

    [mysql] 로컬 내 docker 를 사용하여 mysql 실행 시 명령어

  4. 미리보기
    2020.05.26 - mr.november11

    GitHub Actions 에서 Only actions in <ID> are allowed for this repository 에러가 발생할 경우

  5. 미리보기
    2020.04.18 - mr.november11

    Test

  6. 미리보기
    2020.04.18 - mr.november11

    [React] 모바일에서 input 컴포넌트 클릭 시 자동으로 화면이 확대될 경우 해결 방법

  7. 미리보기
    2020.04.09 - mr.november11

    [React+Spring] 리액트, 스프링 부트로 웹소켓 구현하기

  8. 미리보기
    2020.04.07 - mr.november11

    [번역] 자바스크립트 예제로 배우는 함수형 프로그래밍

1. 현상
아래와 같이 nginx 기본 설정에서는 응답해더에서 nginx 버전 정보가 노출된다.

이 경우 특정 nginx 버전의 보안 취약점을 노린 해커의 공격이 유입될 수 있기 때문에 조치가 필요하다. 

 

2. 조치 방법 

nginx.conf 파일 내 아래 설정을 추가한다. 

server_tokens off;

 

3. 조치 결과 

응답 헤더의 Server 정보에 nginx 버전인 1.19.2 사라졌다. 

다른 카테고리의 글 목록

리뷰/기타 카테고리의 포스트를 톺아봅니다

스프링 부트에서 도커 컨테이너 이미지 생성하기

  • 개발 환경
    • Intellij IDE
    • Maven

참고 사이트 : https://spring.io/guides/gs/spring-boot-docker/

예제 코드 생성

@SpringBootApplication
@RestController
public class DockerTestApplication {

    @RequestMapping("/")
    public String sayHello() {
        return "Hello CI";
    } 

    public static void main(String[] args) {
        SpringApplication.run(DockerTestApplication.class, args);
    }

}
  • maven 을 활용하여 jar 파일 생성 및 실행
$ mvn package && java -jar target/*.jar
$ curl localhost:8080                                        
Hello CI

Dockerfile 생성

  • maven package 로 생성된 jar 파일을 컨테이너 이미지 안에 넣고 엔트리 포인트를 지정한다.
FROM openjdk:8-jdk-alpine
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]

Docker 이미지 생성

  • 실행 명령어 : docker build -t ci/hello-ci .
$docker build -t ci/hello-ci .                                                 SIGINT(2) ↵
Sending build context to Docker daemon  16.75MB
Step 1/4 : FROM openjdk:8-jdk-alpine
8-jdk-alpine: Pulling from library/openjdk
e7c96db7181b: Pull complete
f910a506b6cb: Pull complete
c2274a1a0e27: Pull complete
Digest: sha256:94792824df2df33402f201713f932b58cb9de94a0cd524164a0f2283343547b3
Status: Downloaded newer image for openjdk:8-jdk-alpine
 ---> a3562aa0b991
Step 2/4 : ARG JAR_FILE=target/*.jar
 ---> Running in a50bdf4f6a76
Removing intermediate container a50bdf4f6a76
 ---> a44d7bc3b35f
Step 3/4 : COPY ${JAR_FILE} app.jar
 ---> ca072fc923ee
Step 4/4 : ENTRYPOINT ["java","-jar","/app.jar"]
 ---> Running in d5fb7258f922
Removing intermediate container d5fb7258f922
 ---> 336f8ce8cdc3
Successfully built 336f8ce8cdc3
Successfully tagged ci/hello-ci:latest

Docker 이미지 실행

  • 실행 명령어 : docker run -p 8080:8080 ci/hello-ci
$ docker run -p 8080:8080 ci/hello-ci                                                



  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.3.1.RELEASE)

2020-07-08 00:46:10.959  INFO 1 --- [           main] c.e.dockertest.DockerTestApplication     : Starting DockerTestApplication v0.0.1-SNAPSHOT on 145d1d9f806a with PID 1 (/app.jar started by root in /)
2020-07-08 00:46:10.968  INFO 1 --- [           main] c.e.dockertest.DockerTestApplication     : No active profile set, falling back to default profiles: default
2020-07-08 00:46:13.175  INFO 1 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2020-07-08 00:46:13.211  INFO 1 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2020-07-08 00:46:13.212  INFO 1 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.36]
2020-07-08 00:46:13.399  INFO 1 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2020-07-08 00:46:13.400  INFO 1 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 2305 ms
2020-07-08 00:46:14.013  INFO 1 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2020-07-08 00:46:14.453  INFO 1 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2020-07-08 00:46:14.480  INFO 1 --- [           main] c.e.dockertest.DockerTestApplication     : Started DockerTestApplication in 4.277 seconds (JVM running for 5.158)

다른 카테고리의 글 목록

Spring 카테고리의 포스트를 톺아봅니다

로컬 내 docker 를 사용하여 mysql 실행 시 명령어

 

docker run -p 3306:3306 --name mysql -e MYSQL_ROOT_PASSWORD=password -d mysql mysqld --default-authentication-plugin=mysql_native_password

다른 카테고리의 글 목록

리뷰/기타 카테고리의 포스트를 톺아봅니다

GitHub Actions 에서 Only actions in <ID> are allowed for this repository 에러가 발생할 경우 

Workflow 로그를 확인하면 다음과 같은 문구의 에러가 발생한다. 

Error 

Only actions in "novenber11th" are allowed for this repository 

 

이 경우 Setting -> Actions 메뉴에서 Actions permissions 가 Enable local and third party Actions for this repository 로 설정되어 있는지 확인한다. 에러가 발생할 당시에는 해당 값이 Enable local Actions only for this repository 로 설정되어 있었다.

(메뉴 설명상으로는 문제가 없어야 하는데 왜 그런지는 잘 모르겠다 ..)

 

다른 카테고리의 글 목록

리뷰/기타 카테고리의 포스트를 톺아봅니다

Test

2020. 4. 18. 01:14 - mr.november11

Test

 

다른 카테고리의 글 목록

리뷰/기타 카테고리의 포스트를 톺아봅니다

모바일에서 input 컴포넌트 클릭 시 자동으로 화면이 확대될 경우 해결 방법은 메타데이터 내 user-scalable=no 를 설정하면 된다.

 

CRA로 생성한 리액트 프로젝트 기준으로 다음과 같이 index.html 파일 내 viewport 메타 데이터 정보가 존재한다. 

 

<meta name="viewport" content="width=device-width, initial-scale=1 />

 

여기에 user-scalable=no 를 추가하면 간단하게 문제를 해결할 수 있다. 

 

 <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" />

다른 카테고리의 글 목록

React 카테고리의 포스트를 톺아봅니다

- Back-end : Spring Boot, WebSocket, SockJS, Gradle

- Front-end : ReactJS, react-stomp

 

1. Spring Boot 서버 구현 

1) Spring Boot -> Message -> WebSocket 로 Spring boot 프로젝트를 생성한다. 
   생성 후 프로젝트 내 spring-boot-starter-websocket 이 추가되었는지 확인해야 한다. 

 

'org.springframework.boot:spring-boot-starter-websocket'

 

2) WebSocketConfig 클래스 생성

- enableSimpleBroker : 메시지 브로커를 등록한 

- addEndpoint : 클라이언트가 접속할 웹 소켓의 주소, setAllowedOrigins("*")로 전체 CORS 를 허용하며 SockJS을 사용하도록 설정했다. 

 

@Controller
@Configurable
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.enableSimpleBroker("/topic");
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/test").setAllowedOrigins("*").withSockJS();
    }

}

 

3) 웹소켓을 컨트롤할 TestController 클래스 생성 

- 웹소켓을 주고 받을 시 이벤트를 Trigger 하는 방법은 2가지가 있다. 

   먼저 client 소켓에서 sendMessage 함수로 메시지를 보낼 경우에는 @MessageMapping 어노테이션으로 받을 수 있다.
   두 번째로 @RequestMapping을 사용할 경우에는 웹 소켓이 연동된 Client와 무관하게 외부의 GET 질의로 이벤트를 Trigger 할 수 있다. 

 

- 메시지를 응답하는 방식도 2가지다. 

   첫 번째는 @SendTo 어노테이션을 사용하여 해당 topics를 수신하는 Client Websocket에 메시지를 전달할 수 있다.  
   이 경우 해당 함수는 리턴할 타입을 정의해야하며 반환값 리턴을 통해 Client에 메시지를 전달한다. 

   두 번째는 SimpMessagingTemplate를 사용하는 방식이다.  @SendTo 어노테이션을 사용하지 않고SimpMessagingTemplate로 응답값을 보낼 수 있으며, 이 경우 해당 함수의 리턴값은 void로 처리해야 한다.  SimpMessagingTemplate의 convertAndSend 함수를 사용하면 특정 유저에게만 메시지를 보낼 수 있다. 

 

@RestController
public class TestController {

    @Autowired
    private SimpMessagingTemplate webSocket;

    @MessageMapping("/sendTo")
    @SendTo("/topics/sendTo")
    public String SendToMessage() throws Exception {

        return "SendTo";
    }

    @MessageMapping("/Template")
    public void SendTemplateMessage() {
        webSocket.convertAndSend("/topics/template" , "Template");
    }

    @RequestMapping(value="/api")
    public void SendAPI() {
        webSocket.convertAndSend("/topics/api" , "API");
    }
}

 

2. React 코드 구현

1) 프로젝트 생성

 

npx create-react-app websocket_react

 

2) 관련 라이브러리 추가 

- sockjs-client, react-stomp 라이브러리 추가

 

yarn add sockjs-client react-stomp

 

3) 테스트 App 코드 작성

- react-stomp 라이브러리를 사용하여 웹소켓을 주고 받을 SockJsClient 컴포넌트를 생성한다.

  SockJsClient 생성 시 url은 WebSocket Config에서 Endpoint로 추가한 주소를 지정한다. 
  topics는 서버가 메시지를 보낼 시 수신할 토픽을 지정한다. 다중 지정할 수 있다.

  예제에서는 서버에서 3가지 방식으로 발신하는 모든 메시지를 수신한다. 

 

- SendTo, SendTemplate 버튼은 위 Controller에서 지정한 함수를 트리거하는 이벤트를 작성한다. 

   버튼 클릭 시 SockJsClient의 ref가 지정된 websocket에서 각 sendMessage 함수를 사용하여 서버에 메시지를 보낸다. 

- SockJsClient 관련 Docs : https://github.com/lahsivjar/react-stomp/blob/master/API.md

import React, {useRef} from 'react';

import SockJsClient from 'react-stomp';

function App () {
  const $websocket = useRef (null);

  const handleMsg = msg => {
    console.log (msg);
  };

  const handleClickSendTo = () => {
    $websocket.current.sendMessage ('/sendTo');
  };

  const handleClickSendTemplate = () => {
    $websocket.current.sendMessage ('/Template');
  };

  return (
    <div>

      <SockJsClient
        url="http://localhost:8080/start"
        topics={['/topics/sendTo', '/topics/template', '/topics/api']}
        onMessage={msg => {
          console.log (msg);
        }}
        ref={$websocket}
      />
      <button onClick={handleClickSendTo}>SendTo</button>
      <button onClick={handleClickSendTemplate}>SendTemplate</button>

    </div>
  );
}

export default App;

 

3. 실행 결과

- SendTo 버튼 클릭 시 

 

- SendTemplate 버튼 클릭 시

 

- 외부에서 http://localhost:8080/api 호출 시

다른 카테고리의 글 목록

Spring 카테고리의 포스트를 톺아봅니다

원문 : https://medium.com/@riteshkeswani/a-practical-introduction-to-functional-programming-javascript-ba5bee2369c2

Mary Rose Cook이 작성한 A practical introduction to functional programming을 기반으로 작성한 문서입니다. 원문의 파이썬 코드 대신 자바스크립트 코드 예제로 내용을 설명합니다.


함수형 프로그래밍은 사이드 이펙트(side effect)가 없다는 것이 특징입니다. 이는 함수 외부에 있는 데이터 의존하지 않으며, 함수 외부에 있는 데이터를 변경하지 않는 것을 의미합니다. ‘함수형'의 모든 것은 이러한 속성에서 파생된다는 점을 명심해야 합니다.

다음은 비 함수형 function의 예제입니다.

 

다음은 비 함수형 function의 예제입니다.

 

var a = 0;

function increment1() {
	return a += 1;
}
(역주 : increment1 함수가 외부 데이터 변수인 a 에 의존합니다.)

 

다음은 함수형 function의 예제입니다.

 

increment2(a) {
	return a + 1;
}

 

반복문 대신에 map과 reduce를 사용하십시오.

 

Map

 

map은 함수와 배열 원소를 사용합니다. 먼저 비어있는 배열을 새롭게 생성합니다. 원본 배열의 각 원소에 대해 함수를 실행하고 반환 값을 새로운 배열에 삽입합니다. 모든 과정이 끝나면 새로운 배열을 반환합니다.

아래의 비 함수형 코드는 이름의 배열을 임의로 할당된 코드명으로 변경합니다.

 

var names = [“Mary”, “Isla”, “Sam”];
var code_names = [“Mr. Pink”, “Mr. Orange”, “Mr. Blonde”];

for(var i in code_names) {
	var randomIndex = Math.floor(Math.random() * code_names.length); 
	var randomValue = code_names[randomIndex];
	names[i] = randomValue; 
}
(역주 : 실제 이름 리스트인 names를 반복문 수행 과정 내에서 직접 변경합니다.) 

console.log(names);
# => ["Mr. Blonde", "Mr. Pink", "Mr. Pink"]

 

위 코드를 for 반복문 대신 map 함수를 사용하면 다음과 같습니다.

 

var names = [“Mary”, “Isla”, “Sam”];
var code_names = [“Mr. Pink”, “Mr. Orange”, “Mr. Blonde”];

names = names.map(function(item) {
	var randomIndex = Math.floor(Math.random() * code_names.length); 
	var randomValue = code_names[randomIndex];
	return randomValue; 
});
(역주 : names에 대한 변경은 map 함수가 완료된 후 return 과정에서 이루어집니다.) 

console.log(names);
# => ["Mr. Orange", "Mr. Orange", "Mr. Blonde"]

 

Reduce

 

reduce 또한 함수와 배열 원소를 사용합니다. 각 원소를 조합하여 생성된 값을 반환합니다.

 

다음 예제는 ‘Sam’이라는 단어를 문자열 목록에서 찾아 카운트합니다.

 

var sentences = [
	‘Mary read a story to Sam and Isla’, 
	‘Isla cuddled Sam’, 
	‘Sam chortled’
];

var sam_count = 0;
for(var i in sentences) {
	var results = sentences[i].match(/Sam/g);
	if(results) {
		sam_count += results.length;
	}
}

console.log(sam_count);
# => 3

 

동일한 코드를 reduce로 작성하면 다음과 같습니다.

 

var sentences = [
	‘Mary read a story to Sam and Isla’, 
	‘Isla cuddled Sam’, 
	‘Sam chortled’
];

var sam_count = sentences.reduce(
	function(previousValue, currentValue) {
		var results = currentValue.match(/Sam/g);
		if(results) {
			previousValue += results.length;
		}
		return previousValue; 
	}, 
	0
);

console.log(sam_count);
# => 3

 

map과 reduce의 장점

 

  • 한 줄로 간단하게 처리할 수 있습니다.
  • map과 reduce에서는 반복에서 가장 중요한 부분인 배열, 연산, 반환 값이 항상 같은 위치에 있다.
  • 반복문의 코드는 이전에 선언된 변수에 영향을 미칠 수 있다. map과 reduce는 일반적으로 함수형으로 작동합니다.
    (역주 : map과 reduce는 함수형 프로그래밍 개념에 따라 기존 변수에 대한 사이드 이펙트가 없도록 구현하는 것이 원칙입니다.

  • map과 reduce는 원소 단위의 연산입니다. 반복문은 사람이 한 줄 단위로 로직을 읽고 이해해야 합니다. 반면에 map과 reduce는 복잡한 알고리즘과 원소의 추상적인 상태를 빠르게 이해할 수 있는 블록 단위의 코드를 제공합니다.

  • map과 reduce와 유사한 기능을 가진 다양한 함수(filter, all, any, find 등)가 있습니다.

명령형으로 작성하지 말고 선언형으로 작성하십시오.

아래 예제 프로그램은 세 대의 차량에서 레이스를 진행합니다. 각 단계에서 차량은 앞으로 이동하거나 정지합니다. 프로그램은 각 단계 별 차량의 위치를 출력합니다. 5단계가 진행된 후 레이스를 종료합니다.

 

레이스의 출력 결과는 다음과 같습니다.

 

-
--
--

--
--
---

---
--
---

----
---
----

----
----
-----

 

프로그램 코드는 다음과 같습니다.

 

var time = 5;
var car_positions = [1, 1, 1];

while(time > 0) { 
	time -= 1;
	console.log(‘’); 

	var carsCount = this.car_positions.length;
	for(var i=0; i<carsCount; i++) {
		if(Math.random() > 0.3) {
			this.car_positions[i] += 1;
		}

		var output_str = '- '.repeat(car_position);
		console.log(output_str);
	}
}

 

위 코드는 명령형으로 작성되었습니다. 함수형 프로그래밍은 선언형으로 코드를 작성합니다. 이는 어떻게 하는지에 대한 정의(how)가 아니라 무엇을 할 것인지 정의(what)합니다.

함수형 프로그램밍으로 구현하기

각 코드를 함수로 구현하여 프로그램을 선언형으로 작성할 수 있습니다.

 

function move_cars() {
	var carsCount = car_positions.length; 
	for(var i=0; i<carsCount; i++) {
		if(Math.random() > 0.3) {
			car_positions[i] += 1;
		}
	}
}

function draw_car(car_position) {
	var output_str = '- '.repeat(car_position);
	console.log(output_str);
}

function run_step_of_race() {
	this.time -= 1;
	move_cars(); 
}

function draw() {
	console.log(‘’);
	for(var i in car_positions) {
		draw_car(car_positions[i]);
	}
}

while(time > 0) {
	run_step_of_race();
	draw();
}

 

프로그램의 메인 루프는 다음과 같습니다.

 

“남은 시간이 있다면 레이스를 실행하고 출력합니다. 그리고 시간을 다시 확인합니다.” 각 레이스의 단계에 대해 더 자세히 알고 싶다면 구현된 함수를 읽으면 됩니다.

 

코드 자체가 내용을 설명하고 있기 때문에 주석을 추가하지 않아도 됩니다.

 

코드를 함수로 나누는 것은 코드를 더 읽기 쉽게 만들기 때문에 고민이 필요하지 않습니다.

 

위 코드는 함수를 사용하지만 외부 데이터(변수)에 의존적이기 때문에 함수형 프로그래밍의 조건을 만족하지 않습니다. 코드의 함수는 상태 값을 매개변수로 전달받아 사용하지 않습니다. 이러한 방식은 외부 변수의 변화에 의해 함수의 결괏값이 달라질 수 있습니다. 각 함수가 실제로 무엇을 하는지 확인하기 위해 정독이 필요합니다. 함수 내에서 외부 변수를 사용한다면 변수의 출처를 찾고 다른 함수에서 해당 변수를 변경하는지 확인해야 합니다.

 

상태 제거하기

 

자동차 레이스의 함수형 프로그래밍 버전은 다음과 같습니다.

 

function move_cars(car_positions) {
  return car_positions.map(function(item) {
    if(Math.random() > 0.3) {
      item += 1;
    }
    return item;
  });
}

function draw_car(car_position) {
  var output_str = ‘- ‘.repeat(car_position);
  console.log(output_str);
}

function run_step_of_race(state) {
  state[‘time’] -= 1;
  state[‘car_positions’] = move_cars(state[‘car_positions’]);
  return state; 
}

function draw(state) {
  console.log(‘’);
  state[‘car_positions’].map(function(item) {
    draw_car(item);
    return item;
  });
}

function race(state) {
  draw(state);
  if(state[‘time’] > 0) {
    state = run_step_of_race(state);
    race(state);
  }
}

race({
  ‘time’: 5,
  ‘car_positions’: [1, 1, 1] 
});

 

위 코드는 함수형 프로그래밍으로서 다음과 같은 특징을 갖습니다.

  • 공유 변수를 사용하지 않습니다. time과 car_positions는 race()로 전달됩니다.

  • 함수는 매개 변수를 사용합니다.

  • 함수 내에서 변수가 인스턴스화 되지 않습니다. 모든 데이터는 반환 값을 통해서 변경됩니다. race()는 run_step_of_race()의 결과와 함께 반복됩니다. 각 단계마다 새로운 상태가 생성되어 다음 단계로 전달됩니다.

파이프라인을 사용하십시오.

이전 섹션에서는 일부 명령형 반복문이 보조 함수를 호출하는 재귀 형태로 사용되었습니다. (역주 : race 함수 내에서 race를 다시 호출하는 재귀형 구조입니다.) 이번에는 파이프라인 기법을 활용하여 명령형 반복문을 다른 형태로 다시 작성해보겠습니다.

 

아래의 반복문은 밴드의 이름, 출신을 수정하는 역할을 수행합니다.
(역주 : 출신을 ‘Canada’로 수정하고 이름은 단어 단위로 제일 앞 문자를 대문자로 수정합니다.)

 

var bands = [
  {‘name’: ‘sunset rubdown’, ‘country’: ‘UK’, ‘active’: false},
  {‘name’: ‘women’, ‘country’: ‘Germany’, ‘active’: false},
  {‘name’: ‘a silver mt. zion’, ‘country’: ‘Spain’, ‘active’: true}
];

function format_bands(bands) {
  for (var i in bands) {
    bands[i][‘country’] = ‘Canada’;
    var name = bands[i][‘name’];
    name = name.replace(‘.’, ‘’);
    var nameParts = name.split(‘ ‘);
    for(var j in nameParts) { 
      nameParts[j] = nameParts[j].charAt(0).toUpperCase() +  nameParts[j].slice(1);
    }
    bands[i][‘name’] = nameParts.join(“ “);
  } 
}

format_bands(bands);

//print bands
console.log(JSON.stringify(bands));

 

위 코드 함수에서 ‘format’의 의미는 매우 애매모호합니다. 코드를 살펴보면 동일 반복문 안에서 세 가지 작업이 수행됩니다.

  • ‘country’ 키를 ‘Canada’로 설정합니다.
  • 구두점(.)을 밴드 이름에서 제거합니다.
  • 밴드 이름을 대문자로 변환합니다.

이는 코드가 의도한 바를 파악하기 어렵고 코드가 수행되는 상태를 파악하기 어렵습니다. 이러한 코드는 재사용하기 어렵고 테스트나 병렬 화도 어렵습니다.

다음과 비교해 보겠습니다.

 

pipeline_each(
  bands, 
  [set_canada_as_country, strip_punctuation_from_name, capitalize_names]
)

 

위 코드는 이해하기 더 쉽습니다. 각 기능이 서로 연관되어 있는 것처럼 보입니다. 이전 함수의 출력 결과는 다음 함수의 출력 결과로 입력됩니다. 각 함수가 함수형 프로그램의 특성을 갖고 있다면 재사용, 테스트, 병렬화가 용이합니다.

 

pipeline_each의 역할은 밴드를 set_canada_as_country()와 같은 변환 함수로 한 번에 하나씩 전달하는 것입니다. 함수가 모든 밴드에 적용된 후 pipeline_each는 변환된 밴드를 다음 함수로 전달합니다.

 

변환 함수를 살펴보겠습니다.

 

var set_canada_as_country = function set_canada_as_country(band) {
  band['country'] = "Canada";
  return band;
}

var strip_punctuation_from_name = function strip_punctuation_from_name(band) {
  band['name'] = band['name'].replace('.', '');
  return band;
}

var capitalize_names = function capitalize_names(band) {
  var nameParts = band['name'].split(‘ ‘);
  for(var j in nameParts) { 
    nameParts[j] = nameParts[j].charAt(0).toUpperCase() +  nameParts[j].slice(1);
  }
  band['name'] = nameParts.join(“ “);  
  return band;
}

 

pipeline_each 구현은 다음과 같습니다.

 

function pipeline_each(data, functions) {
  return functions.reduce(
    function(newData, currentFunction) {
      return newData.map(function(item) {
        return currentFunction.call(this, item);
      });
    }, 
    data
  ); 
}

bands = pipeline_each(
  bands, 
  [set_canada_as_country, strip_punctuation_from_name, capitalize_names]
);

console.log(JSON.stringify(bands));

Conclusion

함수형 프로그래밍은 ‘어떻게(how)’가 아니라 ‘무엇(what)’을 정의합니다. 이는 코드를 깔끔하게 추상화하여 나중에 쉽게 최적화할 수 있는 장점이 있습니다.

다른 카테고리의 글 목록

Javascript 카테고리의 포스트를 톺아봅니다