목차
프로젝트를 시작하면 가장 먼저 고민하게 되는 부분 중 하나는 배포 환경이다.
개인 프로젝트나 해커톤처럼 규모가 크지 않은 경우에는 복잡한 인프라를 처음부터 구축할 필요는 없다. 하지만 로컬 환경에만 의존하거나 수동 배포 방식으로 운영하게 되면, 협업이나 실제 서비스 단계에서 불편함이 생기기 쉽다.
이번 글에서는 작은 규모의 프로젝트를 기준으로 AWS와 GitHub Actions를 활용해 기본적인 배포 환경을 구성하는 과정을 정리한다. VPC 기반의 네트워크 분리부터 EC2와 Docker를 이용한 애플리케이션 배포, RDS를 활용한 데이터베이스 구성, 그리고 CI/CD 파이프라인 구축까지 전체 흐름을 단계적으로 살펴볼 예정이다.
또한 AWS 콘솔은 업데이트에 따라 화면 구성이나 설정 위치가 달라지는 경우가 많기 때문에, 현재 구성 기준을 바탕으로 처음부터 끝까지 따라할 수 있도록 정리해보았다.
AWS ( Amzon Web Services ) 란 ?
본격적으로 인프라 구축에 앞서, 이번 글에서 사용할 AWS에 대해 간단히 알아보자.
AWS는 아마존에서 제공하는 클라우드 플랫폼으로, 서버와 데이터베이스, 스토리지, 네트워크 등 다양한 인프라 서비스를 인터넷을 통해 제공한다. 쉽게 말해, 직접 서버를 구매하고 관리하지 않아도 인터넷을 통해 서버, 데이터베이스, 스토리지, 네트워크 등의 인프라를 필요한 만큼 빌려 사용할 수 있는 서비스이다.
과거에는 서비스를 운영하기 위해 물리 서버를 구매하고, 서버실을 구축하고, 네트워크를 직접 구성해야 했다.
하지만 AWS를 사용하면 몇 번의 클릭만으로 서버를 생성하고 데이터베이스를 구축할 수 있으며, 사용할 만큼만 비용을 지불하면 된다.
즉, AWS는 개발자가 인프라 구축에 드는 시간과 비용을 줄이고, 서비스 개발과 운영에 집중할 수 있도록 도와주는 플랫폼이라고 볼 수 있다.
리전(Region)과 가용영역 (AZ)
본격적으로 리소스들을 생성하기 전에 AWS의 리전(Region)과 가용 영역(Availability Zone)에 대해 먼저 이해할 필요가 있다.
리전 (Region)
AWS는 전 세계 여러 지역에 데이터센터를 운영하고 있으며, 이러한 물리적 위치를 리전(Region) 이라고 부른다.
예를 들어, 서울 리전은 ``ap-northeast-2``, 도쿄 리전은 ``ap-northeast-1``, 그리고 버지니아 리전은 ``us-east-1``이다.
일반적으로 서비스 대상 사용자가 한국이라면 서울 리전을 선택하는 것이 지연 시간 (Latency) 측면에서 유리하다.
가용 영역 (Availability Zone)
하나의 리전은 다시 여러 개의 가용 영역 (AZ)으로 구성된다.
가용 영역은 물리적으로 분리된 데이터센터 그룹이라고 생각하면 된다.
서울 리전의 경우, 다음과 같은 AZ를 제공한다.
- ap-northeast-2a
- ap-northeast-2b
- ap-northeast-2c
- ap-northeast-2d
왜 여러 AZ를 사용하는가 ?
만약 모든 리소스를 하나의 AZ에만 배치했는데, 해당 데이터센터에 장애가 발생한다면 서비스 전체가 중단될 수 있다.
이를 방지하기 위해 AWS에서는 여러 AZ에 리소스를 분산 배치하는 구조를 권장한다.
1. VPC ( Virtual Private Cloud )
그럼 이제 본격적으로 AWS 인프라를 구성해보자.
AWS에서 가장 먼저 생성할 리소스는 VPC이다.
VPC는 AWS 내에서 사용하는 독립적인 가상 네트워크 공간으로,
이후 생성할 EC2, RDS와 같은 리소스들이 모두 이 네트워크 내부에 배치된다.
1.1 VPC 생성
VPC를 생성할 때는 VPC 이외 다른 리소스들을 함께 생성해주는 것이 좋다.
VPC는 단독으로 존재해도 의미가 없고, 반드시 Subnet, Route Table, IGW 구조와 함께 설계되어야 실제 네트워크로 동작한다.
AWS Console에 로그인한 후, VPC 탭에 아래와 같은 경로로 접속한다.

처음 VPC 생성 화면에 접속하면 아래와 같은 화면이 나타난다.
AWS에서는 VPC와 함께 여러 네트워크 리소스를 한 번에 생성할 수도 있지만,
이번 글에서는 네트워크 구성을 직접 이해하며 진행하기 위해 [생성할 리소스] 항목에서 [VPC만] 을 선택한다.

이후, 위 사진과 같이 입력 및 선택한다.
일반적으로 VPC는 ``10.0.0.0/16`` 과 같은 CIDR 대역으로 구성한다.
이때 이 CIDR은 이 VPC가 사용할 수 있는 전체 IP 범위를 의미한다.
CIDR 이란 ?
네트워크에서 사용할 IP 주소 범위를 정의하는 표기법이다.
예를 들어, `10.0.0.0/16` 은 다음 범위를 의미한다.
``10.0.0.0 ~ 10.0.255.255``
즉, 약 65,536개의 IP 주소를 사용할 수 있는 네트워크이다.
AWS에서 VPC를 생성할 때 CIDR 블록을 지정하고, 이후 해당 범위 내에서 Public Subnet, Private Subnet 등을 나누어 사용하게 된다.
예를 들어 다음과 같이 환경별로 VPC를 분리할 수 있다.
- Production VPC → `10.0.0.0/16`
- Staging VPC → `10.1.0.0/16`
- Development VPC → `10.2.0.0/16`
이처럼 환경을 분리하는 이유는 단순히 IP를 나누기 위해서가 아니라, 네트워크를 독립적으로 관리하고 보안을 분리하기 위함이다.
왜 VPC CIDR을 보통 `/16` 으로 설정하는가 ?
AWS에서는 VPC CIDR을 `/8` 과 같이 매우 크게 설정할 수도 있지만, 일반적으로는 거의 사용하지 않는다.
`10.0.0.0/8` 은 다음 범위를 의미한다.
``10.0.0.0 ~ 10.255.255.255``
즉, 약 1,677만 개의 IP를 사용할 수 있는 매우 큰 네트워크 공간이다.
처음 보면 “그럼 `/8`이 더 좋은 것 아닌가?”라고 생각할 수 있다.
하지만 실제로는 IP 부족 때문이 아니라 네트워크 설계와 관리 문제 때문에 `/8`은 잘 사용하지 않는다.
만약 첫 번째 VPC에서 `10.0.0.0/`8 을 사용해버리면, 이후 생성하는 다른 VPC들은 모두 같은` 10.x.x.x `대역을 사용할 수 없거나, VPC Peering / Transit Gateway 구성 시 CIDR 충돌 문제가 발생할 수 있다.
AWS에서는 여러 VPC를 연결하는 경우가 많기 때문에, CIDR이 겹치면 네트워크 연결이 불가능하거나 복잡한 추가 설정이 필요해진다.
또한 `/16` 단위로 VPC를 나누면 다음과 같이 서브넷 설계가 훨씬 명확해진다.
- Public Subnet → ALB, NAT Gateway
- Private Application Subnet → ECS
- Private Database Subnet → RDS, ElastiCache
특히 고가용성을 위해 최소 2개의 AZ를 사용하는 경우, 각 계층별로 여러 서브넷이 필요해지기 때문에 /16 정도의 공간이 가장 균형이 좋다.
즉, AWS에서는 IP를 많이 확보하기 위해 /8을 쓰는 것이 아니라, 확장성과 네트워크 분리, 충돌 방지, 운영 관리 편의성 때문에 /16을 기본으로 사용하는 경우가 많다.
또한 VPC는 생성 이후 CIDR 변경이 어렵기 때문에, 처음 설계 단계에서 충분한 여유를 두고 구성하는 것이 중요하다.
1.2 Subnet 구성
서브넷은 VPC 내부 IP 범위를 나누는 단위이며, 외부 노출 여부에 따라 구조가 완전히 달라진다.
단순히 서브넷을 생성했다고 해서 인터넷과 통신할 수 있는 것은 아니다.
외부 인터넷과 통신하기 위해서는 인터넷 게이트웨이(IGW) 와 라우팅 테이블(Route Table) 이 함께 구성되어야 한다.
인터넷 게이트웨이는 VPC와 인터넷을 연결하는 역할을 하고, 라우팅 테이블은 들어오고 나가는 트래픽을 어느 경로로 전달할지 결정한다.
즉, 서브넷에 인터넷 게이트웨이로 향하는 라우팅 경로를 연결하면 외부 인터넷과 통신할 수 있게 된다.
반대로 해당 경로가 없다면 외부 인터넷과 직접 통신할 수 없으며, 이를 일반적으로 Private Subnet이라고 부른다.
이제 Subnet 을 구성해보도록 하자.
Subnet은 Private Subnet과 Public Subnet을 각각 다른 가용 영역에 1개씩 구성할 것이다.
이때, 하나의 Subnet은 반드시 하나의 AZ에만 속한다는 점을 주의해야 한다.

위 사진에서 1번과 같은 경로로 이동하면, 다음과 같은 화면이 나타날 것이다.
오른쪽 [서브넷 생성] 버튼을 눌러 서브넷을 구성해보자.

- 서브넷을 생성할 VPC를 선택한다.
이때 앞에서 생성한 VPC를 선택하면 된다. - 서브넷 이름을 작성한다.
서브넷 이름은 어떤 용도의 서브넷인지 쉽게 파악할 수 있도록 VPC 이름과 어느 정도 통일하여 작성하는 것이 좋다. 위 이미지 하단에 있는 이름 태그를 사용해도 좋지만, 나는 잘 사용하지 않는 편이다. - 가용 영역(AZ)을 선택한다.
이번 글에서는 ``ap-northeast-2a``와``ap-northeast-2c``를 사용할 예정이다.
AWS에서는 고가용성을 위해 서로 다른 가용 영역에 리소스를 분산 배치하는 것을 권장한다. - 서브넷의 CIDR 대역을 설정한다.
서브넷의 CIDR은 VPC 범위 내에서 설정해야 하며, 다른 서브넷과 겹치지 않도록 한다. 예를 들어, VPC를 `10.0.0.0/16`으로 생성했다면, 다음과 같이 서브넷을 나눌 수 있다.- Public Subnet - 가용영역 A : `10.0.1.0/24`
- Private Subnet - 가용영역 A : `10.0.101.0/24`
- Public Subnet - 가용영역 C : `10.0.2.0/24`
- Private Subnet - 가용영역 C : `10.0.102.0/24`
- [서브넷 생성] 버튼을 눌러 서브넷을 생성해준다.
위 과정을 4번 반복해 Public Subnet과 Private Subnet을 2개씩 생성해준다.

다 생성했으면, 위 사진처럼 리소스 맵에서 내가 생성한 VPC 내 서브넷들이 가용영역 별로 생성된 것을 확인할 수 있다.
1.3 인터넷 게이트웨이 (IGW) 생성

위 1번 경로로 이동해 [인터넷 게이트웨이 생성] 버튼을 누른다.

인터넷 게이트웨이 이름을 입력해주고, [인터넷 게이트웨이 생성] 버튼을 눌러 생성한다.
현재 상태는 인터넷 게이트웨이(IGW)만 생성된 상태이다.
인터넷 게이트웨이는 VPC와 연결되어야 사용할 수 있으므로, 앞서 생성한 VPC에 연결해주도록 하자.
다만, 이것만으로 외부 통신이 가능한 것은 아니며 이후 라우팅 테이블 설정도 함께 진행해야 한다.


위 이미지의 번호 순서대로 진행하여 인터넷 게이트웨이를 VPC에 연결한다.
1.4 라우팅 테이블 구성
앞서 설명했듯이 Public Subnet은 인터넷과 직접 통신할 수 있는 서브넷이다.
따라서 인터넷 게이트웨이(IGW)로 향하는 기본 경로 `0.0.0.0/0`가 설정된 라우팅 테이블을 생성한 뒤, 해당 라우팅 테이블을 서브넷에 연결하면 Public Subnet으로 동작하게 된다. 반면 Private Subnet은 인터넷 게이트웨이로 향하는 경로를 설정하지 않는다. 따라서 외부에서 직접 접근할 수 없으며, 내부 네트워크를 통해서만 통신할 수 있다.
즉, Public Subnet과 Private Subnet을 구분하는 핵심은 서브넷 자체가 아니라 어떤 라우팅 테이블이 연결되어 있는지에 있다.
만약 Private Subnet의 리소스가 패키지 설치나 AWS API 호출 등을 위해 인터넷에 접근해야 하는 경우에는 NAT Gateway를 통해 외부로 나가는 경로를 구성할 수 있지만, 이번 글에서는 다루지 않을 예정이다.
라우팅 테이블은 VPC를 만들면, 자동으로 생성된다. 따라서, 우리는 해당 라우팅 테이블을 수정해주면 된다.
[VPC] > [라우팅 테이블] > [생성된 라우팅 테이블] 선택 하여 해당 탭으로 이동하는 방법도 있지만,
[VPC] > [내 VPC] > [리소스 맵] > [라우팅 테이블] 선택하여 이동하는 방법을 좀 더 추천한다.

위 이미지처럼 해당 탭으로 이동하면, 현재 라우팅 정보가 보일 것이다.
현재는 ``Destination``이 `10.0.0.0/16` 이고, ``Target``이 `local`인 AWS가 자동으로 만들어주는 VPC 내부 통신용 라우트만 존재하는 것을 확인할 수 있다.
💡 10.0.0.0/16 →local
의미 : 목적지가 VPC 내부 IP 라면 VPC 내부 네트워크를 통해 전달해라
이제 우측에 하단에 있는 [라우팅 편집] 을 눌러 라우팅 테이블을 수정해보자.

위 사진처럼 `0.0.0.0/0`을 목적지(Destination)로 설정하고, 대상을 인터넷 게이트로 지정한다.
여기서 `0.0.0.0/0`은 모든 IP 주소를 의미한다.
즉, 현재 VPC 내부 네트워크 (`10.0.0.0/16`)을 제외한 모든 외부 트래픽을 인터넷 게이트웨이로 전달하라는 뜻이다.
예를 들어 EC2 인스턴스가 구글이나 깃허브와 같은 외부 서비스에 접근하려고 할 때, 해당 요청은 `0.0.0.0/0 → 인터넷 게이트웨이` 라우팅 규칙을 통해 인터넷으로 전달된다.
이 라우팅 규칙이 연결된 서브넷은 외부 인터넷과 통신할 수 있게 되며, 이를 Public Subnet이라고 부른다.
반대로 `0.0.0.0/0 → 인터넷 게이트웨이` 경로가 없는 서브넷은 외부 인터넷과 직접 통신할 수 없으며, 이를 Private Subnet 이라고 부른다.
라우팅 테이블을 수정했으면, 이제 해당 라우팅 테이블을 Public Subnet과 연결해줘야 한다.

[작업] > [서브넷 연결 편집] 을 통해 연결할 서브넷을 수정한다.

위 사진처럼 Public Subnet 을 선택해주고, [연결 저장] 버튼을 눌러 연결해준다.
다시 [VPC] > [내 VPC] 선택 > [리소스 맵] 화면을 확인해보면 한 가지 이상한 점을 발견할 수 있다.
Private Subnet들도 Public Subnet과 동일하게 인터넷 게이트웨이에 연결되어 있는 것처럼 보인다는 점이다.
처음 보면 설정이 잘못된 것처럼 보일 수 있지만, 실제로는 정상적인 상태이다.
모든 서브넷은 반드시 하나의 라우팅 테이블과 연결되어 있어야 한다. 현재 VPC에는 우리가 수정한 라우팅 테이블만 존재하기 때문에 Public Subnet뿐만 아니라 Private Subnet도 동일한 라우팅 테이블을 사용하고 있다. 문제는 현재 라우팅 테이블에 `0.0.0.0/0 → 인터넷 게이트웨이` 규칙이 포함되어 있다는 점이다.
따라서 현재 상태에서는 Private Subnet 역시 외부 인터넷과 통신할 수 있는 상태이며, Public Subnet과 동일하게 동작하게 된다.
이를 해결하기 위해서는 Private Subnet 전용 라우팅 테이블을 별도로 생성해야 한다.

[VPC] > [라우팅 테이블] 탭으로 이동한 뒤 우측 상단에 있는 [라우팅 테이블 생성] 버튼을 클릭한다.
위 사진처럼 이름을 입력하고, 앞서 생성한 VPC를 선택한 뒤 라우팅 테이블을 생성한다.
해당 라우팅 테이블은 별도의 라우팅 규칙을 추가하지 않고, 기본 라우팅 정보만 존재하면 된다.
즉, VPC 내부 리소스들과는 통신할 수 있지만 인터넷 게이트웨이로 향하는 경로가 없기 때문에 외부 인터넷과는 직접 통신할 수 없다.

생성이 완료되면 이전에 퍼블릿 라우팅 테이블을 수정했을 때와 같은 방법으로 해당 라우팅 테이블을 프라이빗 서브넷과 연결해준다.
이렇게 하면 Public Subnet은 인터넷과 통신할 수 있고, Private Subnet은 외부에서 직접 접근할 수 없는 구조가 완성된다.

💡 참고 )
기존에 public subnet을 연결했던 라우팅 테이블의 이름을 `my-public-route-table`로 수정해 라우팅 테이블명을 통일해주었다.
2. 보안그룹 설정
지금까지는 VPC, Subnet, 인터넷 게이트웨이, 리우팅 테이블을 구성하여 네트워크가 어떻게 연결되는지 설정하였다.
하지만 네트워크 경로가 열려있다고 해서 모든 통신이 허용되는 것은 아니다.
AWS에서는 보안 그룹 (Security Group)을 통해 어떤 트래픽을 허용할 것인지 제어할 수 있다.
보안그룹은 AWS 리소스에 적용되는 가상 방화벽이라고 생각하면 된다.
예를 들어, Public Subnet에 EC2를 배치하고 인터넷 게이트웨이와 연결했다고 하더라도, 보안 그룹에서 HTTP(80) 또는 HTTPS(443) 포트를 허용하지 않으면 외부 사용자는 해당 서버에 접근할 수 없다. 반대로 라우팅 테이블을 통해 네트워크 경로가 존재하지 않는다면, 보안그룹에서 포트를 허용하더라도 통신은 이루어질 수 없다.
즉, 실제 통신이 가능하려면 다음 두 조건을 모두 만족해야 한다.
- 네트워크 경로(Route)가 존재해야 한다.
- 보안그룹(Security Group)에서 해당 트래픽을 허용한다.
보안 그룹은 크게 2가지 규칙으로 구성된다.
인바운드 규칙 (Inbound Rule)
외부에서 리소스로 들어오는 트래픽을 제어한다.
예를 들어, 사용자가 웹 브라우저를 통해 서버에 접속하는 경우, 인바운드 규칙이 적용된다.
아웃바운드 규칙 (Outbound Rule)
리소스에서 외부로 나가는 트래픽을 제어한다.
반대로, EC2 인스턴스가 깃허브에서 코드를 내려받거나 외부 API 를 호출하는 경우에는 아웃바운드 규칙이 적용된다.
이제 보안그룹을 직접 생성해보자.

[VPC] > [보안그룹] 탭으로 이동하면 위 사진처럼 VPC 생성 시 자동으로 생성되는 보안그룹이 존재할 것이다.
기본 보안 그룹은 같은 보안 그룹에 속한 리소스끼리는 자유롭게 통신할 수 있도록 설정되어 있으며, 특별한 설정 없이도 동작한다.
하지만 실제 운영 환경에서는 이를 권장하지 않는다.
따라서 서비스별로 별도의 보안 그룹을 생성하여 관리하는 것이 좋다.
이번 글에서는 보안그룹을 다음과 같이 2개로 분리할 예정이다.
- MyApp-sg
- MyDB-sg
2.1 Application 보안그룹 생성
위 사진에서 우측 상단에 있는 [보안 그룹 생성] 버튼을 클릭한다.

위 사진처럼 보안그룹 이름, 설명을 입력해주고, 이전에 생성한 VPC를 선택한다.
이후, 인바운드 규칙을 설정해준다. 각 규칙에 대해 알아보자.
- SSH (22) : 원격으로 서버에 접속하기 위한 프로토콜
- 터미널을 통해 EC2 서버에 접속할 때 사용된다.
- 서버 설정, 로그 확인, Docker 실행 등의 작업을 수행하기 위해 필요하다.
- 만약 실제 배포를 하게 된다면 `0.0.0.0/0`으로 열어두지 않고, 본인 IP 또는 팀원의 IP만 허용하는 것이 좋다.
- HTTP (80) : 웹 브라우저가 웹 서버와 통신할 때 사용하는 기본 프로토콜이다.
- 사용자가 ``http://app.com`` 과 같은 사이트에 접속하면 `80`번 포트를 통해 요청이 전달된다.
- 최근에는 대부분 HTTPS를 사용하지만, HTTP 요청을 HTTPS로 리다이렉트하기 위해 여전히 사용되는 경우가 많다.
- HTTPS (443) : HTTP에 SSL/TLS 암호화를 적용한 프로토콜이다.
- 사용자가 ``https://app.com`` 과 같은 사이트에 접속하면 `443`번 포트를 통해 요청이 전달된다.
- 로그인 정보나 개인정보가 암호화되어 전송되므로 실제 서비스에서는 반드시 사용해야 한다.
- Custom TCP 3000 : Node.js 애플리케이션이 실행되는 포트이다. (Spring Boot로 백엔드를 구축할 경우 이는 생략해도 된다.)
- Custom TCP 8080 : Spring Boot 애플리케이션이 실행되는 포트이다. (Node.js로 백엔드를 구축할 경우 이는 생략해도 된다.)

아웃바운드 규칙은 별도로 설정하지 않고, 기본 설정을 유지한다.
애플리케이션 서버는 생각보다 다양한 외부 서비스와 통신한다. 깃허브에서 코드를 다운로드하고, npm 패키지를 설치하고, 외부 API를 호출하는 등이 모두 아웃바운드 통신이다. 만약 아웃바운드 규칙까지 엄격하게 제한하면 이러한 기능들이 정상적으로 동작하지 않을 수 있다.
다만, 금융권이나 공공기간과 같은 보안 요구사항이 높은 환경에서는 아웃바운드도 제한하는 경우가 있으니 참고하면 좋을 것 같다.
이제 우측 하단에 [보안 그룹 생성] 을 클릭해 보안그룹을 생성해준다.
2.2 Database 보안그룹 생성
이제 DB용 보안그룹을 생성해보자. DB용 보안그룹은 MySQL이 실행될 RDS에 연결할 보안그룹이다.
데이터베이스는 외부에 직접 노출하지 않고, 서버에서만 접근할 수 있도록 최소한의 포트만 허용한다.
위와 같은 방법으로 인바운드 규칙을 설정해주고, 보안그룹을 생성해보자.

Application용 보안그룹을 생성했을 때와 마찬가지로,
이름, 설명을 작성해주고, VPC를 선택한 뒤, 인바운드 규칙을 생성해 보안그룹을 생성해준다.
Database용 보안그룹은 데이터베이스에 접근할 수 있는 대상을 제한하는 것이 가장 중요하다.
HTTP(80), HTTPS(443)와 같은 웹 포트는 허용하지 않고, MySQL이 사용하는 `3306`포트만 허용한다.
이때 소스 (Source)는 앞서 생성한 Application 용 보안그룹으로 지정한다. 즉, Application 서버에 연결된 리소스만 Database에 접근할 수 있다는 의미이다.
우리는 흔히 배포된 DB 즉, RDS에 접근할 때 Datagrip이나 Workbench를 이용해 접근한다. 하지만, 위와 같이 소스를 Application 용 서버로 설정해두면, Datagrip이나 Workbench를 이용한 원격 접속이 불가능하다.
물론 개발이나 운영 과정에서 데이터 확인이 필요한 경우도 있다. 이럴 때는 SSH 터널링을 이용해 EC2를 경유하여 접속하거나, 특정 개발자의 공인 IP만 일시적으로 허용하여 접근하는 방법을 사용할 수 있다.
이번 글에서는 데이터베이스 보안을 우선하여 Application 서버만 접근할 수 있도록 구성하겠다.
이제 Database용 보안그룹까지 생성했으면, 애플리케이션과 데이터베이스 간 통신을 위한 네트워크 환경은 어느 정도 준비된 상태이다.
3. EC2 구축
이제 실제 애플리케이션을 실행할 서버를 생성해보자.
AWS에서 가상 서버를 생성하고 실행할 수 있는 서비스가 바로 EC2 (Elastic Compute Cloud) 이다.
EC2는 AWS가 제공하는 가상 머신 서비스로, 사용자가 원하는 운영체제와 사양을 선택해 서버를 생성할 수 있다.
쉽게 말해, 애플리케이션을 배포하고 실행하기 위한 클라우드 서버라고 생각하면 된다.
먼저, 우측 상단에 아래처럼 현재 리전이 서울 리전인지 확인하자.

AWS의 모든 리소스는 특정 리전에 생성된다. 따라서 다른 리전이 선택된 상태에서 작업을 선택하면, 앞서 생성한 VPC, 서브넷, 보안그룹 등의 리소스가 보이지 않을 수 있다. 또한 EC2는 생성 시 동일한 리전 내의 VPC와 서브넷만 연결할 수 있으므로, 현재 작업 중인 인프라가 위치한 리전과 동일한 리전을 선택해야 한다.
3.1 EC2 인스턴스 생성

먼저, 위 사진처럼 [EC2] > [인스턴스] 탭으로 이동해 [인스턴스 시작] 버튼을 클릭한다.

일단, 이름을 작성해주고, `Ubuntu`를 선택해 프리티어를 제공하는 사양을 선택한다.
학생은 돈을 아껴야 하기 때문에 ....... 최대한 프리티어를 사용하도록 구성한다 ....... !!!!

인스턴스 유형 또한 프리티어인 인스턴스 유형을 선택해준다.
EC2랑 통신할 때에는 반드시 키 페어로 로그인을 해야 보안 문제가 발생하지 않는다.
키 페어를 이미 생성했으면 해당 키 페어를 선택하고, 그렇지 않을 경우에는 새 키 페어를 생성한다.

키 페어 이름을 생성하고 키 페어 유형과 키 파일 형식을 선택한 뒤, [키 페어 생성] 버튼을 클릭해 키 페어를 생성한다.
키 페어는 EC2에 SSH로 접속하기 위해 필요하다.
이는 공개키와 개인 키로 구성되며, 생성 시 다운로드되는 ``.pem``파일이 개인키 역할을 한다.
한 번 생성한 키 페어는 여러 EC2 인스턴스에서 재사용할 수 있으므로, 이미 생성한 키 페어가 있다면 새로 만들 필요는 없다.
단, ``.pem``파일은 생성 시에만 다운로드할 수 있으므로 안전한 위치에 반드시 보관해야 한다.

다음으로 네트워크 설정 단계에서 앞서 생성한 VPC를 선택해준다.
그리고 서브넷은 반드시 Public Subnet을 선택한다.
또한 보안 그룹은 이전에 생성한 Application 보안 그룹을 선택해준다.
스토리지 설정에서는 프리티어 기준 최대 용량인 30GiB를 입력한 뒤,
마지막으로 우측에 있는 [인스턴스 시작] 버튼을 클릭하면 EC2 생성이 완료된다.

이렇게 인스턴스가 생성된 것을 확인할 수 있다.
( NGINX 를 사용하는 경우 ) 3.2 탄력적 IP 설정
EC2 인스턴스를 생성하면 기본적으로 Public IP가 자동으로 할당된다
(앞서 우리가 인스턴스를 생성할 때, 자동으로 할당되도록 설정해줬기 때문 !!)
하지만 IP는 이 인스턴스를 중지했다가 다시 시작하면 변경될 수 있다.
즉, 서버를 재시작할 때마다 IP가 바뀌기 때문에 프론트엔드 연결이나 도메인 설정이 필요한 경우에는 불편해진다.
이 문제를 해결하기 위해 사용하는 것이 탄력적 IP (Elastic IP)이다.
탄력적 IP는 AWS에서 제공하는 고정 Public IP로, EC2 인스턴스에 연결해두면 인스턴스를 재시작해도 동일한 IP를 유지할 수 있다.
NGNIX를 사용할 때만 필요한 건가 ?
여기서 헷갈릴 수 있는 부분이 있는데, 탄력적 IP는 NGINX를 사용할 때만 필요한 것이 아니다.
핵심 기준은 NGINX 여부가 아니라 다음이다:
- 외부에서 EC2에 고정된 주소로 접근해야 하는지
- 프론트엔드 / 도메인 / API 연동이 필요한지
NGINX를 사용하는 구조에서는 보통 EC2가 외부 요청의 진입점 역할을 하기 때문에 고정 IP가 필요해지는 경우가 많아 함께 언급되는 것이다.

[EC2] > [탄력적 IP] 탭으로 이동해 우측 상단에 있는 [탄력적 IP 주소 할당] 버튼을 클릭한다.

위 사진과 같이 탄력적 IP는 기본 설정을 건드리지 않고 바로 할당해주면 된다.


생성된 탄력적 IP를 이전에 생성한 EC2 인스턴스에 연결하면 된다.
이렇게 하면 해당 EC2는 이제 고정된 Public IP를 가지게 되며, 외부에서 항상 동일한 주소로 접근할 수 있는 환경이 구성된다.
이제 EC2 인스턴스에 접속하여 NGINX를 설치하고, 브라우저에서 정상적으로 접근되는지 확인해보자.
( NGINX 를 사용하는 경우 ) 4. NGINX 설치 및 브라우저 접속
먼저 EC2에 접속하는 방법은 여러 가지가 있다.
하지만 나는 AWS 콘솔에서 직접 인스턴스에 접속하는 방식을 주로 사용한다.
SSH 키 설정 문제를 피할 수 있고, 별도 설정 없이 바로 접속할 수 있어 가장 안정적인 방법으로 사용했다.


[EC2 인스턴스 연결] 탭에서 [연결]버튼을 클릭하면 콘솔 화면이 열린다.

위 사진과 같이 콘솔이 나오는 것을 확인할 수 있다.
이후 해당 콘솔에 다음 명령어를 통해 NGINX를 설치한다.
sudo apt update
sudo apt upgrade -y
sudo apt install nginx -y
- `apt update` : 패키지 목록 최신화
- `apt upgrade` : 기존 패키지 업데이트
- `apt install nginx` : NGINX 웹 서버 설치
설치가 완료되면 EC2 인스턴스의 퍼블릭 IP를 브라우저에 입력해 NGINX 기본 페이지가 정상적으로 출력되는지 확인한다.

(사실 단 한 번의 오류 없이 바로 성공해서 당황스럽다 ...... 🥵)
지금까지 사이드 프로젝트 또는 장기 해커톤을 기준으로, AWS 기반의 기본 인프라 환경을 구성해보았다.
VPC를 통해 네트워크를 설계하고, Public / Private Subnet을 분리하여 기본적인 네트워크 구조를 구성했다.
또한 인터넷 게이트웨이와 라우팅 테이블을 통해 외부와 통신 가능한 환경을 만들었으며, 보안 그룹을 통해 애플리케이션과 데이터베이스 간 접근을 제어하는 구조도 함께 설계했다.
이후 EC2 인스턴스를 생성하여 실제 애플리케이션을 실행할 수 있는 서버 환경을 구성하고, 탄력적 IP를 적용하여 고정된 접속 주소를 확보했다. 마지막으로 NGINX를 설치하여 웹 서버가 정상적으로 동작하는 것까지 확인했다.
이제 단순한 로컬 개발 환경을 넘어, 외부에서 접근 가능한 기본적인 클라우드 인프라를 갖춘 상태이다.
다만 현재 구조는 애플리케이션 서버 중심의 초기 구성 단계이며,
실제 서비스 운영을 위해서는 데이터베이스 구성과 애플리케이션 간 안정적인 연결 구조가 추가로 필요하다.
다음 글에서는 RDS를 활용한 데이터베이스 구축과 SSH 터널링을 통한 안전한 접근 방식에 대해 다뤄볼 예정이다.
이를 통해 외부 노출 없이도 안전하게 데이터베이스를 관리하도록 구성해볼 예정이다.
그럼 안농 .
'DevOps' 카테고리의 다른 글
| [Infra/DevOps] 사이드 프로젝트를 위한 AWS 인프라 구축 가이드 (2026 ver.) #3- CICD, Github Actions, Docker (1) | 2026.06.16 |
|---|---|
| [Infra/DevOps] 사이드 프로젝트를 위한 AWS 인프라 구축 가이드 (2026 ver.) #2- RDS, SSH 터널링 (0) | 2026.06.14 |
| [AWS] ECS 이용하여 ECR에 수동으로 배포해보기 (+ ECS, ECR 개념 정리) (0) | 2025.11.05 |
| [AWS] IAM 권한 : 그룹 권한과 인라인 권한 차이 정리 (0) | 2025.10.26 |
| [AWS] AWS의 정의와 주요 서비스 (0) | 2025.10.18 |