본문 바로가기
개발/Github Actions 이메일 뉴스레터 제작기

Github Actions로 이메일 뉴스레터 만들기 (1)

by 개발곰 2021. 11. 14.

먼저 제 글과 이번 프로젝트는 jwn4492님의 Github Action으로 매일마다 브리핑 받기!을 기반해서 만들었습니다. 감사합니다.
소스코드는 이 곳입니다.

왜 GitHub Actions를 썼는가?

Github actions는 여러분의 git 저장소에 있는 코드를 원하는 조건(예) cron, push, pull request, issue 작성) 등의 일이 생겼을 때 지정해놓은 명령어를 실행시켜주는 기능입니다. 테스트 통과여부를 측정하는 코드를 작성할 수도 있고, 아니면 결과물을 빌드해서 다른 곳에 올려주는 코드를 작성할 수 있습니다. 아니면 단순하게 작성한 코드를 정기적으로 실행시키는 역할만 해도 되죠.
제가 하기로 한 일은 대학공지들 중 최근 공지들을 긁어와서 그 목록을 보여주는 일이었습니다. 매번 정적으로 읽어와서 따로 별도 파일이나 DB에 저장하는 일없이 바로 이메일로 보내는 것이 목적이기 때문에 Github Actions를 사용했습니다.

대학 공지 긁어오기

최근에 자바스크립트 공부를 하고 있었기 때문에 자바스크립트에 익숙해질 겸, node.js 위에서 돌아가도록 짰습니다. 웹 크롤링을 위해 여러 라이브러리가 사용되고 있었지만, 대상 페이지가 로그인 없이 접속할 수 있고, 제 목적은 공지 내용을 보는게 아니라 공지목록을 긁어오는 것이기 때문에, puppeteer같은 복잡한 기능을 가진 라이브러리는 필요없다고 판단하여 사용 경험이 있는 axios를 사용하였습니다.

const getHTML = async () => {
    try {
        return await axios.get(endpoint);
    } catch (error) {
        console.error(error);
    }
};

그리고 이 함수에 .then() 체이닝으로 단계 별로 값을 넘겨주는 식으로 코드를 작성하였습니다. 받아온 html 데이터에서 필요한 부분을 선택하는 데에는 cheerio를 사용했습니다. 다른 사람의 코드를 참고하는 과정에서 비슷하게 짜다보니 라이브러리도 비슷한 걸 쓰게 되더라고요.

getHTML()
    .then((html) => {
        let ulList = [];
        const $ = cheerio.load(html.data);
        const $bodyList = $("#txt > div > div.no-more-tables > table > tbody > tr");
        $bodyList.each(function (i, elem) {
            ulList[i] = {
                fixed: isNaN($(this).find("td:nth-child(1)").text()),
                title: $(this).find("td.subject > a").text(),
                date: new Date(Date.parse($(this).find("td:nth-child(5)").text())),
            };
        });

        return ulList;
    })

그 이후 Array.prototype.filter(), Array.portotype.map(), Array.prototype.reduce()를 사용해서 하나의 문자열로(<ul>로 묶여있는 리스트 HTML 태그) 만들어서 bodyList란 이름으로 이메일 보내는 부분으로 넘겨주었습니다.

이메일 보내기

이메일을 보내는 데 사용한 라이브러리는 nodemailer였습니다. 학교 계정(Office 365)을 활용해서 진행 했는데, nodemailer에서 smtp 설정이 이미 들어 있더라고요. 그리고 이미 들어있지 않은 서비스도 간단하게 smtp 연결이 가능했습니다.

    .then((bodyList) => {
        const today = new Date();
        const header = `${today.getMonth() + 1}월 ${today.getDate()}일 GIST 대학 공지`;
        const mailbody = `<h1><a href="${endpoint}">${header}</a></h1>` + "\n" + bodyList;
        const transporter = nodemailer.createTransport({
            service: "Outlook365",
            auth: {
                user: ID,
                pass: PASSWORD,
            },
        });

        const mailOption = {
            from: ID,
            to: ID,
            subject: header,
            html: mailbody,
        };

        try {
            transporter.sendMail(mailOption);
        } catch (error) {
            console.error(error);
        }
    });

ID와 패스워드 값은 따로 저장해 놓았습니다. github secret와 actions의 workflow파일에서 환경변수로 이 값들을 넘겨줄 수 있거든요. node.js에서 시스템 환경변수에 접근하는 방법이 궁금하시면, process.env로 검색하시면 정보를 찾을 수 있습니다.

Github actions 설정

Github actions의 설정은 /.github/workflows/에 yml 방식으로 저장됩니다.

# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions

name: Node.js CI

on:
# 테스트시에만 작동
#    push:
#        branches: [master]
    schedule:
        # 오후 8시, 매주 월수금 안내를 받게 됩니다.
        - cron: "0 11 * * 1,3,5"
jobs:
  build:

    runs-on: ubuntu-latest

    strategy:
      matrix:
        node-version: [12.x]

    steps:
    - uses: actions/checkout@v2
    - name: Use Node.js ${{ matrix.node-version }}
      uses: actions/setup-node@v1
      with:
        node-version: ${{ matrix.node-version }}
    - run: npm ci
    - run: npm start
      env:
        CI: true

        EMAIL: ${{ secrets.EMAIL }}
        PASSWORD: ${{ secrets.PASSWORD }}

여기서 env는 환경 변수를 전달해주는 부분으로, secrets를 통해 Github secrets에 암호화되어 저장된 내용에 접근할 수 있게 해줍니다. 이 값들은 저장소의 setting에 있는 Secrets에서 추가/삭제가 가능합니다.
그리고 사소한 주의사항이지만 찾기 힘든 사항인데, cron에 불필요한 띄어쓰기가 있으면 안됩니다. 이 오류 찾느라 꽤 걸렸어요.

Github secrets

처음에는 테스트를 위해 push시에도 작동하게 했지만, 정상적으로 작동한 것을 확인하고 나서는 cron을 제외하면 비활성화 했습니다.

README에 뱃지 달기

create badge

이렇게 실행된 Github Actions의 결과를 뱃지로 제공해줍니다. 저는 현재 문제없이 코드가 작동중이라 passing이 뜨네요.

Node.js CI

마치며

도착한 이메일


실제로 도착한 이메일입니다. 예쁘게 도착했네요.
Github Actions는 여러분이 별도 DB나 파일에 접근하지 않아도 되는 각종 작업들을 자동화 할때 굉장히 유용한 도구입니다. 테스트/빌드 작업에 쓸 수도 있지만, 간단한 챗봇이나 뉴스레터 구현에도 사용할 수 있죠. 여러분도 한 번 이런 기능을 사용해 보시면 합니다.