AWS EC2 에서 R 설치하고 사용하기

목적은 AWS EC2 를 통해서 베포하고 있는 내 웹사이트에서 DB에 저장된 데이터를 이용해서 통계를 돌리는 겁니다.

그리고 그 통계를 웹페이지에 실제로 나타내는 것이 저의 계획이지요.

이를 위해서 먼저, 상단 메뉴의 웹사이트 > 아마존으로 웹사이트 배포하기 내부에 있는 포스팅을 참고하시길 바랍니다.

우선 R 설치를 위해서는 내 EC2 에 SSH 접속이 필요합니다.

R 설치하기

				
					sudo dnf install -y R

				
			

저의 EC2 는 Amazon Linux 3 버전입니다.

무료 버전 micro를 이용하고 있다면, 저와 동일할 겁니다.

위 명령어를 입력해서 R 을 EC2에 다운로드 합시다.

약간 시간이 걸리는데 기다려주세요.

				
					Type 'demo()' for some demos, 'help()' for on-line help, or
'help.start()' for an HTML browser interface to help.
Type 'q()' to quit R.

>
  zlib-devel-1.2.11-33.amzn2023.0.5.x86_64
  zziplib-0.13.72-1.amzn2023.0.3.x86_64

Complete!
				
			

기다리면 이런 식으로 다운로드에 성공했다는 메시지가 나타납니다.

이럼 다시 명령창에 명령어를 입력 가능합니다.

				
					R

				
			

명령창에 R 이라는 명령어를 실행하면 이제 R을 실행할 수 있습니다. 

실행하게 되면 SSH 명령어의 앞에 있던 [ec2-user-@ip … ] 이 사라지고 > 이라는 기호가 나타날 겁니다.

여기에 script를 작성하면 되는 건데요.

SSH 접속해서 R을 사용할 계획은 없으니 다시 나가줍시다. 

				
					> q()
Save workspace image? [y/n/c]: n

				
			

위처럼 q() 라는 명령어를 실행합시다.

그리고 n 을 누르면 저장 안 하고 나갈 수 있습니다. 

y는 저장하고 나가기, c 는 취소입니다. 

R 사용하기

				
					R

				
			

이 R을 사용하기 위해서는 필요한 라이브러리를 설치할 필요가 있습니다.

우선은 2개의 변수와, 그 변수에 해당하는 데이터를 이용한 T-test 를 실행하는 것을 목적으로 해보겠습니다.

이를 위해 다시 R을 실행하겠습니다. 

				
					install.packages("dplyr")

				
			
				
					install.packages("jsonlite")


				
			
				
					install.packages("knitr")

				
			

그리고 위와 같이 R에서 기존에 사용하던 명령어 그대로 패키지를 설치 가능합니다.

이때 “Would you like to use a personal library instead?” 라는 질문이 나오는데 yes 눌러줍시다.

이럼 EC2 인스턴스의 home 에 디렉토리가 생성이 됩니다. 

그리고 selection 단계가 있는데 Korea 는 49입니다. 

				
					library(dplyr)

# 가상의 데이터 생성
group1 <- rnorm(100, mean = 50, sd = 10)
group2 <- rnorm(100, mean = 55, sd = 10)

# T-test 수행
test_result <- t.test(group1, group2)

# 결과 출력
print(test_result)

				
			

설치가 완료 되었다면 다음의 코드를 이용해서 잘 되나 한 번 볼까요?

이런 식으로 t-test 결과가 나타난다면 성공입니다.

제대로 R이 EC2에 설치가 되었으며 이것을 이용해서 통계 결과를 낼 수 있게 된 겁니다.

다만, 현재는 SSH 접속을 하고 해당 Putty 창에서 가능한 거라 처음 계획했던 것과는 거리가 있습니다.

이제 프론트엔드 및 백엔드에서 어찌 이용하면 좋을지 살펴보겠습니다. 

프론트엔드 및 백엔드에서 이용하기

우선은 프론트엔드를 한 번 세팅해봅시다.

저는 사용자가 특정한 변수와 그 변수에 해당하는 데이터를 백엔드에 전달하면 그 데이터를 가지고 특정한 통계가 진행될 수 있도록 하고 싶습니다.

이를 위해서 다음과 같이 페이지를 구성했습니다.

				
					import React, { useState } from "react";

const Script = () => {
  const data = {
    a: [1, 2, 3, 4, 5, 6, 7, 8, 9],
    b: [9, 8, 7, 6, 5, 4, 3, 2, 1],
    c: [5, 5, 5, 5, 5, 5, 5, 5, 5],
  };

  const [selectedKeys, setSelectedKeys] = useState({
    key1: "a",
    key2: "b",
  });

  const handleSelectChange = (e) => {
    const { name, value } = e.target;
    setSelectedKeys({ ...selectedKeys, [name]: value });
  };

  const DoTtest = async () => {
    if (selectedKeys.key1 === selectedKeys.key2) {
      alert("다른 두 개의 키를 선택해주세요.");
      return;
    }

    try {
      const payload = {
        data1: data[selectedKeys.key1],
        data2: data[selectedKeys.key2],
      };

      const response = await fetch("your api", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(payload),
      });

      if (response.ok) {
        const result = await response.json();
        console.log(result);
      } else {
        alert("Something went wrong!");
      }
    } catch (error) {
      alert(error);
    }
  };

  return (
    <div>
      <select
        name="key1"
        value={selectedKeys.key1}
        onChange={handleSelectChange}
      >
        {Object.keys(data).map((key) => (
          <option key={key} value={key}>
            {key}
          </option>
        ))}
      </select>

      <select
        name="key2"
        value={selectedKeys.key2}
        onChange={handleSelectChange}
      >
        {Object.keys(data).map((key) => (
          <option key={key} value={key}>
            {key}
          </option>
        ))}
      </select>

      <button onClick={DoTtest}>T-test 실행</button>
    </div>
  );
};

export default Script;

				
			

정말 대충 설정한 데이터가 Object 형태로 data 라는 변수에 지정되어 있습니다.

key 값으로는 총 3개를 설정해뒀죠? 이 key 값에 할당된 각각의 value를 2개 짝지어서 t-test 를 수행할 겁니다.

지금 이것을 위해서 생성한 로직이 DoTtest 라는 로직입니다.

이 로직은 특정 라우터와 함께 동작하며, 그 라우터에서 전송한 결과를 콘솔창에 나타내죠.

이를 위한 백엔드 라우터도 제작하겠습니다.

				
					const { exec } = require("child_process");

app.post("/your api", (req, res) => {
  const { data1, data2 } = req.body;

  // R 스크립트 파일 경로
  const scriptPath = "/r/ttest_script.R";

  // R 스크립트 실행 명령어
  const command = `Rscript ${scriptPath} ${data1.join(",")} ${data2.join(",")}`;

  exec(command, (error, stdout, stderr) => {
    if (error) {
      console.error(`exec error: ${error}`);
      return res.status(500).send("R 스크립트 실행 중 에러가 발생했습니다.");
    }

    // R 스크립트 실행 결과를 파싱하여 JSON으로 변환
    try {
      const result = JSON.parse(stdout);
      res.json(result);
    } catch (parseError) {
      console.error(`결과 파싱 에러: ${parseError}`);
      res.status(500).send("결과 파싱 중 에러가 발생했습니다.");
    }
  });
});

				
			

이 백엔드는 특정한 경로에 저장한 R script 파일을 사용하여 t-test 를 수행하고 그 결과를 전송할 겁니다.

경로의 경우 Filezilla 를 통해 EC2 인스턴스에 접속해서 디렉토리를 생성해서 그곳에 파일을 업로드 하면 됩니다. 

home/ec2-user/… /script.R 과 같은 식으로 경로를 지정할 수 있겠네요.

이를 위해서 작성한 R 스크립트의 내용은 아래와 같습니다. 

				
					args <- commandArgs(trailingOnly = TRUE)
data1 <- as.numeric(unlist(strsplit(args[1], ",")))
data2 <- as.numeric(unlist(strsplit(args[2], ",")))

result <- t.test(data1, data2)

# 결과를 JSON 형태로 출력
cat(jsonlite::toJSON(list(
  p.value = result$p.value,
  estimate = result$estimate,
  conf.int = result$conf.int,
  statistic = result$statistic,
  parameter = result$parameter,
  null.value = result$null.value,
  alternative = result$alternative,
  method = result$method,
  data.name = result$data.name
)))


				
			

마치며...

위 코드들이 서로 상호작용 하며 작동하도록 했을 때는 이런 결과가 나타나게 됩니다. 

실제 console 창에 각종 Object 형태로 여러 값이 전달되고 있음을 파악 가능합니다.