728x90

728x90
728x90

AWS IAM과 Azure AD는 각각 Amazon Web Services와 Microsoft Azure 클라우드 플랫폼에서 사용자 인증과 권한 부여를 관리하며, 클라우드 기반의 아키텍처를 보다 안전하게 운영할 수 있게 도와준다. 서비스의 선택은 사용하는 클라우드 플랫폼, 필요한 기능, 그리고 조직의 요구사항에 따라 결정될 수 있다.

 

Azure Active Directory(Azure AD)는 Microsoft Azure의 클라우드 기반 ID 및 액세스 관리 서비스로, 사용자가 클라우드 서비스에 안전하게 로그인하고 리소스에 접근하도록 한다. 사용자는 한 번의 로그인으로 Microsoft Azure, Office 365, 그리고 수천 가지의 SaaS 애플리케이션에 접근한다.

 

Azure Active Directory(Azure AD)는 비밀번호 기반, 다중 요소 인증 (MFA), 통합 Windows 인증 등 다양한 인증 방식을 지원하고, 외부 애플리케이션 및 서비스의 인증을 중앙에서 관리하고, 사용자 및 그룹에 따라 액세스를 제어한다.

 

그리고 사용자의 위치, 사용하는 장치, 액세스 시도하는 애플리케이션 등의 조건에 따라 보안 정책을 설정하고, 외부 비즈니스 파트너나 고객에게 액세스 권한을 안전하게 제공하고, 사용자 경험을 맞춤화한다.

 

AWS IAM(Identity and Access Management)은 Amazon Web Services의 보안 서비스로, 조직의 리소스에 대한 액세스를 안전하게 제어한다.

 

AWS IAM은 개별 사용자를 생성하고, 그룹으로 사용자를 관리해 특정 권한을 그룹 단위로 할당한다. IAM 역할을 사용해 사용자, 애플리케이션, 서비스 간에 임시 권한을 할당한다. IAM 정책을 통해 이러한 역할 및 사용자에게 특정 AWS 리소스에 대한 접근 권한을 세밀하게 제어한다.

 

보안을 강화하기 위해 사용자의 계정에 MFA를 추가해 두 단계 인증 과정을 요구하고 AWS CloudTrail을 통해 사용자의 요청 및 API 호출 기록을 로깅하고, 이를 모니터링해 보안을 강화한다. 그리고 다른 AWS 서비스와의 통합을 제공해, EC2 인스턴스나 S3 버킷과 같은 서비스에 대한 접근 권한을 관리한다.

 

AWS Certificate Manager(ACM)는 AWS에서 SSL/TLS 인증서를 쉽게 관리할 수 있도록 돕는 서비스이다. ACM을 사용하면 인증서를 생성하고, 관리하며, 배포하는 과정을 자동화할 수 있다. 이 서비스는 특히 AWS 리소스와 통합되어 사용하기에 편리하다. 여기서는 ACM이 어떻게 Elastic Load Balancer (ELB) 및 Amazon CloudFront (CF)와 같은 서비스와 함께 사용되는지 설명하겠다.

AWS Certificate Manager와 Elastic Load Balancer(ELB)는 AWS에서 제공하는 로드 밸런싱 서비스로, 인터넷 트래픽을 애플리케이션 서버로 분산시킨다. ELB는 높은 가용성과 자동 확장성을 제공하여, 애플리케이션의 부하가 증가할 때도 안정적으로 서비스를 제공할 수 있다. AWS Certificate Manager와 통합하면 ELB를 사용하여 SSL/TLS 인증서를 쉽게 구현할 수 있다. ACM에서 SSL/TLS 인증서를 발급받고, 이를 ELB에 적용하여 보안 통신을 활성화할 수 있다. 이렇게 함으로써, 사용자의 데이터 보호와 애플리케이션의 보안 수준을 높일 수 있다.

 

AWS Certificate Manager와 Amazon CloudFront(CF)는 AWS의 콘텐츠 전송 네트워크 (CDN) 서비스로, 전 세계 데이터 센터에 콘텐츠를 캐시하여 사용자에게 빠르게 콘텐츠를 제공한다. ACM을 이용하면 CloudFront 배포에 SSL/TLS 인증서를 쉽게 적용할 수 있다. 사용자가 ACM을 통해 SSL/TLS 인증서를 생성하고, 이 인증서를 CloudFront에 연동하면 HTTPS를 통한 안전한 콘텐츠 전송을 구성할 수 있다. 이는 사용자 데이터의 암호화 및 전송 보안을 강화하는 데 중요하다.

 

AWS Certificate Manager를 사용하면 ELB와 CloudFront와 같은 서비스에 대한 SSL/TLS 인증서의 구매, 설치 및 갱신과정을 자동화할 수 있어 관리 부담을 크게 줄일 수 있다. 이를 통해 개발자와 시스템 관리자는 보안 설정에 더 적은 시간을 할애하고, 다른 중요한 작업에 더 많은 자원을 투입할 수 있다.

 

 

++ 갠적으로 좋은 답변이라 넣어본 사진

 

728x90

'기타' 카테고리의 다른 글

백준 알고리즘 문제풀이 스트릭 100, tistory 글 안써지는 issue fix  (0) 2024.05.19
use CDN  (0) 2024.04.23
Primitives, TLS, SSL  (0) 2024.04.15
Azure와 MobaXterm을 사용한 배포  (0) 2024.04.11
728x90

Primitives, 즉 암호학적 기본 요소는 암호학 시스템을 구성하는 기본적인 함수나 알고리즘을 의미한다. 데이터 보안과 관련된 다양한 작업에 사용되며, 보다 복잡한 암호 시스템과 프로토콜을 구축하는 데 필요한 구성 요소로 사용된다. 일반적인 암호학적 기본 요소에는 해시 함수, 메시지 암호, 디지털 서명 알고리즘 등이 포함되어있다.

 

해시 함수는 임의 길이의 데이터를 고정된 길이의 해시값으로 매핑하는 함수다. SHA(Secure Hash Algorithm)시리즈가 널리 사용된다. 해시 함수는 데이터의 무결성을 검증하는 데 사용된다. 원본 데이터에서 조금이라도 변경이 발생하면 해시값이 크게 달라진다. 해시는 암호화된 형태로 되돌릴 수 없는 일방향 함수의 특성을 갖는다.

 

비밀번호 저장 같은 보안 관련 작업에서도 중요한 역할을 한다. 비밀번호는 해시된 형태로 저장되고, 로그인 시 입력된 비밀번호를 해시하여 저장된 해시값과 비교함으로써 사용자를 인증한다.

 

메시지 암호화는 데이터 보안을 위해 정보를 암호화된 형태로 변환하는 과정이다. 이 과정을 통해, 데이터가 민감한 정보를 포함하고 있더라도 외부로부터의 불법 접근으로부터 안전하게 보호할 수 있다. 암호화된 메시지는 특정 키를 가진 사용자만이 복호화할 수 있으며, 대칭키 암호, 비대칭키 암호로 구성되어 있다.

 

비밀키 암호(대칭키 암호)암호화와 복호화에 같은 키를 사용하는 방식이다. 가장 유명한 예로 AES(Advanced Encryption Standard)가 있다. 통신하는 양쪽 모두가 동일한 비밀키를 공유하고 있어야 하며, 이 키의 보안이 매우 중요하다. 키 관리가 쉽고 처리 속도가 빠르다는 장점이 있지만, 키를 안전하게 교환하는 것이 어렵다는 단점이 있다.

 

공개키 암호(비대칭키 암호)암호화와 복호화에 서로 다른 키를 사용한다: 공개키와 비밀키. 공개키는 모두에게 공개되어 있고, 비밀키는 개인이 비밀리에 보유한다. RSA가 대표적인 예이다. 공개키로 데이터를 암호화하면 해당 데이터는 해당 비밀키로만 복호화될 수 있다. 이 방식은 키 교환 문제를 해결하지만, 비밀키 암호보다 처리 속도가 느리다.

 

디지털 서명은 문서나 메시지의 무결성과 송신자의 신원을 확인하기 위해 사용된다.

  1. 송신자는 메시지의 해시값을 생성한다.
  2. 생성된 해시값을 송신자의 개인 비밀키로 암호화한다. 이 암호화된 해시값이 서명이 된다.
  3. 암호화된 메시지와 함께 서명을 수신자에게 전송한다.
  4. 수신자는 송신자의 공개키를 사용하여 서명을 복호화하고, 메시지의 해시값을 다시 계산한다. 복호화된 해시값과 새로 계산된 해시값이 일치하면 메시지가 변경되지 않았음을 확인하고, 서명이 유효함을 의미한다.

디지털 서명은 메시지의 진위를 검증하고, 메시지가 송신 경로 중에 변조되지 않았음을 보증한다. 또한, 서명은 송신자가 메시지 발송을 부인할 수 없게 만드는 '부인 방지' 기능도 한다. 이러한 기능은 법적 문서, 소프트웨어 배포, 온라인 트랜잭션 등 다양한 분야에서 중요하게 활용된다.

 

TLS/SSL(Secure Sockets Layer/Transport Layer Security)인증서는 웹사이트와 사용자 간의 통신을 암호화하여 데이터의 안전을 보장한다. 이는 데이터의 무결성을 유지하고, 외부 공격자로부터의 정보 유출을 방지한다.

무료 TLS/SSL 인증서 발급 기관

  1. Let's Encrypt:
    • 가장 인기 있는 무료 인증서 발급 기관 중 하나다.
    • 자동화된 프로세스를 통해 무료 SSL/TLS 인증서를 제공하며, 90일마다 갱신해야 한다.
    • ACME(Automatic Certificate Management Environment) 프로토콜을 사용하여 인증서 설치 및 갱신을 자동화한다.
  2. Cloudflare:
    • 웹사이트 성능 및 보안 서비스를 제공하는 회사로, 사용자가 Cloudflare의 서비스를 이용하면 자동으로 SSL/TLS 보호를 제공한다.
    • Cloudflare를 통해 트래픽이 라우팅될 때, HTTPS를 사용하여 보안 연결을 유지한다.
  3. ZeroSSL:
    • Let's Encrypt와 유사하게 무료 SSL/TLS 인증서를 제공한다.
    • 웹 기반 인터페이스를 통해 인증서를 쉽게 발급받고 관리할 수 있다.

 

무료 TLS/SSL 인증서는 작은 웹사이트나 개인 프로젝트에 적합하며, 웹사이트의 신뢰성과 보안을 높이는 데 큰 도움을 준다.

  1.  
728x90

'기타' 카테고리의 다른 글

백준 알고리즘 문제풀이 스트릭 100, tistory 글 안써지는 issue fix  (0) 2024.05.19
use CDN  (0) 2024.04.23
Azure AD, AWS IAM, ACM  (0) 2024.04.18
Azure와 MobaXterm을 사용한 배포  (0) 2024.04.11
728x90

가상 페이지라고도 불리는 “페이지”는, 4,096 바이트(페이지 크기)의 길이를 가지는 가상 메모리의 연속된 영역이다. 페이지는 반드시 페이지에 정렬(page-aligned)되어 있어야 한다. 즉, 각 페이지는 페이지 크기(4KiB)로 균등하게 나누어지는 가상 주소에서 시작해야 한다는 말이다.

 

그러므로 64비트 가상주소의 마지막 12비트는 페이지 오프셋(또는 그냥 “오프셋”)이다. 상위 비트들은 페이지 테이블의 인덱스를 표시하기 위해 쓰인다. 64비트 시스템은 4단계 페이지 테이블을 사용하는데, 아래 그림과 같은 가상주소를 만들어준다.

63          48 47            39 38            30 29            21 20         12 11         0
+-------------+----------------+----------------+----------------+-------------+------------+
| Sign Extend |    Page-Map    | Page-Directory | Page-directory |  Page-Table |    Page    |
|             | Level-4 Offset |    Pointer     |     Offset     |   Offset    |   Offset   |
+-------------+----------------+----------------+----------------+-------------+------------+
              |                |                |                |             |            |
              +------- 9 ------+------- 9 ------+------- 9 ------+----- 9 -----+---- 12 ----+
                                          Virtual Address

 

각 프로세스는 KERN_BASE (0x8004000000) 미만의 가상주소값을 가지는 독립적인 유저(가상)페이지 집합을 가진다. 반면에 커널(가상)페이지 집합은 전역적이며, 어떤 쓰레드나 프로세스가 실행되고 있든 간에 항상 같은 위치에 남아 있다. 커널은 유저 페이지와 커널 페이지 모두에 접근할 수 있지만, 유저 프로세스는 본인의 유저 페이지에만 접근할 수 있다.

 

물리 프레임 또는 페이지 프레임이라고도 불리는 프레임은, 물리 메모리 상의 연속적인 영역이다. 페이지와 동일하게, 프레임은 페이지사이즈여야 하고 페이지 크기에 정렬되어 있어야 한다. 그러므로 64비트 물리주소는 프레임 넘버와 프레임 오프셋(또는 그냥 오프셋)으로 나누어질 수 있다. 아래 그림처럼 말이다.

                          12 11         0
    +-----------------------+-----------+
    |      Frame Number     |   Offset  |
    +-----------------------+-----------+
              Physical Address

 

x86-64 시스템은 물리주소에 있는 메모리에 직접적으로 접근하는 방법을 제공하지 않는다. Pintos는 커널 가상 메모리를 물리 메모리에 직접 매핑하는 방식을 통해서 이 문제를 해결한다 - 커널 가상메모리의 첫 페이지는 물리메모리의 첫 프레임에 매핑되어 있고, 두번째 페이지는 두번째 프레임에 매핑되어 있고, 그 이후도 이와 같은 방법으로 매핑되어 있다. 그러므로 커널 가상메모리를 통하면 프레임들에 접근할 수 있다.

 

페이지 테이블은 CPU가 가상주소를 물리주소로, 즉 페이지를 프레임으로 변환하기 위해 사용하는 자료구조이다. 페이지 테이블 포맷은 x86-64 아키텍쳐에 의해 결정되었다. Pintos는 threads/mmu.c안에 페이지 테이블을 관리하는 코드를 제공한다.

 

아래 도표는 페이지와 프레임 사이의 관계를 나타낸다. 왼쪽에 보이는 가상주소는 페이지 넘버와 오프셋을 포함하고 있다. 페이지 테이블은 페이지 넘버를 프레임 넘버로 변환하며, 프레임 넘버는 오른쪽에 보이는 것처럼 물리주소를 획득하기 위한 미수정된 오프셋과 결합되어 있다.

                           +----------+
          .--------------->|Page Table|-----------.
         /                 +----------+           |
        |   12 11 0                               V  12 11 0
    +---------+----+                         +---------+----+
    | Page Nr | Ofs|                         |Frame Nr | Ofs|
    +---------+----+                         +---------+----+
     Virt Addr   |                            Phys Addr    ^
                  \_______________________________________/

 

스왑 슬롯은 스왑 파티션 내의 디스크 공간에 있는 페이지 크기의 영역이다. 하드웨어적 제한들로 인해 배치가 강제되는 것(정렬)이 프레임에서보단 슬롯에서 더 유연한 편이지만, 정렬한다고 해서 별다른 부정적인 영향이 생기는 건 아니기 때문에 스왑 슬롯은 페이지 크기에 정렬하는 것이 좋다.

 

각 자료구조에서 각각의 원소가 어떤 정보를 담을지를 정해야 했었다. 또한 자료구조의 범위를 지역(프로세스별)으로 할지, 전역(전체 시스템에 적용)으로 할지도 정해야 하고, 해당 범위에 필요한 인스턴스의 수도 결정해야 한다. 설계를 단순화하기 위해, non-pageable 메모리 (calloc 이나 malloc 에 의해 할당된)에 이러한 자료구조들을 저장할 수 있다.

 

userprog/process.c에 있는 load_segment와 lazy_load_segment를 구현해야 했다. 실행파일로부터 세그먼트가 로드되는 것을 구현하고, 구현된 모든 페이지들은 지연적으로 로드될 것이다. 즉 이 페이지들에 발생한 page fault를 커널이 다루게 된다는 의미이다.

 

program loader의 핵심인 userprog/process.c 의 load_segment loop 내부를 수정해야 했다. 루프를 돌 때마다 load_segment는 대기 중인 페이지 오브젝트를 생성하는vm_alloc_page_with_initializer를 호출한다. Page Fault가 발생하는 순간은 Segment가 실제로 파일에서 로드될 때 이다.

static bool
load_segment(struct file *file, off_t ofs, uint8_t *upage,
			 uint32_t read_bytes, uint32_t zero_bytes, bool writable)
{
	ASSERT((read_bytes + zero_bytes) % PGSIZE == 0);
	ASSERT(pg_ofs(upage) == 0);
	ASSERT(ofs % PGSIZE == 0);

	while (read_bytes > 0 || zero_bytes > 0)
	{
		/* Do calculate how to fill this page.
		 * We will read PAGE_READ_BYTES bytes from FILE
		 * and zero the final PAGE_ZERO_BYTES bytes. */
		size_t page_read_bytes = read_bytes < PGSIZE ? read_bytes : PGSIZE;
		size_t page_zero_bytes = PGSIZE - page_read_bytes;

		/* TODO: Set up aux to pass information to the lazy_load_segment. */
		struct lazy *aux = (struct lazy*)malloc(sizeof(struct lazy));
		aux->file = file;
		aux->ofs = ofs;
		aux->read_bytes = page_read_bytes;
		aux->zero_bytes = page_zero_bytes;

		if (!vm_alloc_page_with_initializer (VM_ANON, upage,
					writable, lazy_load_segment, aux)){
			return false;
		}

		/* Advance. */
		read_bytes -= page_read_bytes;
		zero_bytes -= page_zero_bytes;
		upage += PGSIZE;
		ofs += page_read_bytes;
	}
	return true;
}

 

VM 시스템은 mmap 영역에서 페이지를 lazy load하고 mmap된 파일 자체를 매핑을 위한 백업 저장소로 사용해야 한다. 이 두 시스템 콜을 구현하려면 vm/file.c에 정의된 do_mmap과 do_munmap을 구현해서 사용해야 한다.

 

fd로 열린 파일의 오프셋(offset) 바이트부터 length 바이트 만큼을 프로세스의 가상주소공간의 주소 addr 에 매핑 한다.
전체 파일은 addr에서 시작하는 연속 가상 페이지에 매핑된다. 파일 길이(length)가 PGSIZE의 배수가 아닌 경우 최종 매핑된 페이지의 일부 바이트가 파일 끝을 넘어 "stick out"된다. page_fault가 발생하면 이 바이트를 0으로 설정하고 페이지를 디스크에 다시 쓸 때 버린다.


성공하면 이 함수는 파일이 매핑된 가상 주소를 반환한다. 실패하면 파일을 매핑하는 데 유효한 주소가 아닌 NULL을 반환해야 한다.

void *
do_mmap (void *addr, size_t length, int writable,
		struct file *file, off_t offset) {


	struct file* new_file = file_reopen(file);
	if(new_file == NULL){
		return NULL;
	}

	void* return_address = addr;
	
	size_t read_bytes;
	if (file_length(new_file) < length){
		read_bytes = file_length(new_file);
	} else {
		read_bytes = length;
	}

	size_t zero_bytes = PGSIZE - (read_bytes%PGSIZE);
	
	ASSERT (pg_ofs (addr) == 0);
	ASSERT (offset % PGSIZE == 0);
	ASSERT ((read_bytes + zero_bytes) % PGSIZE == 0);

	while (read_bytes > 0 || zero_bytes > 0) {
			/* Do calculate how to fill this page.
			* We will read PAGE_READ_BYTES bytes from FILE
			* and zero the final PAGE_ZERO_BYTES bytes. */
		size_t page_read_bytes = read_bytes < PGSIZE ? read_bytes : PGSIZE;
		size_t page_zero_bytes = PGSIZE - page_read_bytes;

		struct lazy* file_lazy = (struct lazy*)malloc(sizeof(struct lazy));
		file_lazy->file = new_file;
		file_lazy->ofs = offset;
		file_lazy->read_bytes = page_read_bytes;
		file_lazy->zero_bytes = page_zero_bytes;

		if (!vm_alloc_page_with_initializer(VM_FILE, addr, writable, lazy_load_segment, file_lazy)){
			return NULL;
		}		

		/* Advance. */
		read_bytes -= page_read_bytes;
		zero_bytes -= page_zero_bytes;
		addr += PGSIZE;
		offset += page_read_bytes;
	}
	return return_address;
}

 

지정된 주소 범위 addr에 대한 매핑을 해제한다.
지정된 주소는 아직 매핑 해제되지 않은 동일한 프로세서의 mmap에 대한 이전 호출에서 반환된 가상 주소여야 한다.

void
do_munmap (void *addr) {
    struct supplemental_page_table *spt = &thread_current()->spt;
    struct page *page = spt_find_page(spt, addr);

	int page_count;
	if (file_length(&page->file)%PGSIZE != 0){
 	    page_count = file_length(&page->file) + PGSIZE;
	} else {
		page_count = file_length(&page->file);
	}

    for (int i = 0; i < page_count/PGSIZE; i++)
    {
        if (page)
            destroy(page);
        addr += PGSIZE;
        page = spt_find_page(spt, addr);
    }
}

 

file-backed 페이지의 내용은 파일에서 가져오므로 mmap된 파일을 백업 저장소로 사용해야 한다. 즉, file-backed 페이지를 evict하면 해당 페이지가 매핑된 파일에 다시 기록된다.

 

파일에서 콘텐츠를 읽어 kva 페이지에서 swap in한다. 파일 시스템과 동기화해야 한다.

static bool
file_backed_swap_in (struct page *page, void *kva) {
	struct file_page *file_page = &page->file;
	struct lazy* aux = (struct lazy*)page->uninit.aux;

	lock_acquire(&filesys_lock);
	bool result = lazy_load_segment(page, aux);
	lock_release(&filesys_lock);

	return result;
}

 

내용을 다시 파일에 기록하여 swap out한다. 먼저 페이지가 dirty인지 확인하는 것이 좋다. 더럽지 않으면 파일의 내용을 수정할 필요가 없다. 페이지를 교체한 후에는 페이지의 더티 비트를 꺼야 한다.

static bool
file_backed_swap_out (struct page *page) {
	struct file_page *file_page = &page->file;
	struct lazy* aux = (struct lazy*)page->uninit.aux;
	struct file * file = aux->file;

	if(pml4_is_dirty(thread_current()->pml4,page->va)){
		file_write_at(file,page->va, aux->read_bytes, aux->ofs);
		file_write_at(file_page->file,page->va, file_page->read_bytes, file_page->ofs);
		pml4_set_dirty(thread_current()->pml4, page->va, false);
	}
	page->frame->page = NULL;
	page->frame = NULL;
	pml4_clear_page(thread_current()->pml4, page->va);
	return true;
}

 

https://github.com/yunsejin/PintOS_RE/tree/Project3_RE

 

GitHub - yunsejin/PintOS_RE: PintOS_Project1,2,3

PintOS_Project1,2,3. Contribute to yunsejin/PintOS_RE development by creating an account on GitHub.

github.com

 

728x90
728x90

웹 워커(Web Workers)는 자바스크립트에서 멀티스레딩을 구현할 수 있는 기능이다. 자바스크립트는 기본적으로 싱글 스레드로 동작하기 때문에, 복잡하고 시간이 많이 걸리는 작업을 수행할 때 UI가 멈추지 않도록 별도의 백그라운드 스레드에서 코드를 실행할 필요가 있다.

 

웹 워커를 사용하면 메인 스레드가 아닌 별도의 스레드에서 스크립트를 실행할 수 있어, 메인 스레드(주로 UI를 담당)는 사용자 인터랙션과 화면 업데이트에 집중할 수 있고, 워커 스레드에서는 계산이 많이 필요한 작업을 동시에 처리할 수 있다. 이로 인해 애플리케이션이 더욱 빠르고 반응적으로 동작할 수 있다.

 

웹 워커는 데이터를 메인 스레드와 메시지 형태로 교환하며, 이 메시지는 복사되어 전달되므로 메인 스레드와 워커 스레드 사이에 데이터를 공유하지 않는다. 워커 내에서는 DOM에 직접 접근할 수 없으며, 메인 스레드에서 전달받은 데이터를 기반으로 작업을 수행하고 결과를 메인 스레드로 다시 보낸다.

 

예를 들어, 이미지 처리나 파일 다운로드와 같은 무거운 작업을 워커에서 처리하면 메인 스레드는 이러한 처리가 완료될 때까지 멈추지 않고 계속하여 사용자와의 상호작용을 유지할 수 있다.

 

자바스크립트는 싱글 스레드 언어로, 기본적으로 한 번에 하나의 작업만 수행할 수 있다. 그러나 비동기 프로그래밍 모델을 사용하여 동시성(concurrency)을 구현하고, 여러 작업을 효율적으로 처리할 수 있다. 이를 위한 주요 메커니즘으로 이벤트 루프, 콜백 함수, 프로미스(Promises), async/await 등이 있다.

 

자바스크립트의 실행 환경인 브라우저나 Node.js는 이벤트 루프를 사용하여 연속적인 작업을 처리한다. 이벤트 루프는 콜 스택(Call Stack), 이벤트 큐(Event Queue), 백그라운드 작업을 관리한다.

 

콜 스택이 비어있을 때, 이벤트 루프는 이벤트 큐에서 콜백 함수를 하나씩 콜 스택으로 옮겨 실행한다. 이러한 구조 덕분에 자바스크립트는 비동기 콜백을 활용하여 여러 작업을 동시에 처리할 수 있다.

 

프로미스는 비동기 작업의 최종 성공 또는 실패를 나타내는 객체이다. 콜백 지옥을 피하고, 비동기 코드의 가독성과 작성 편의성을 높이는 데 도움을 준다. 프로미스는 세 가지 상태를 가진다. (대기(pending), 이행(fulfilled), 거부(rejected))

const promise = new Promise((resolve, reject) => {
  if (/* 조건 */) {
    resolve(value); // 작업 성공
  } else {
    reject(error); // 작업 실패
  }
});

promise.then(result => {
  console.log(result); // 성공 결과 처리
}).catch(error => {
  console.error(error); // 에러 처리
});

 

 

async/await는 프로미스 기반의 비동기 코드를 동기 코드처럼 쉽게 작성할 수 있게 해주는 ES2017(ES8)의 문법이다. async로 함수를 정의하면 그 함수는 자동으로 프로미스를 반환한다. await 키워드는 프로미스가 처리될 때까지 함수 실행을 일시 중지하고, 결과 값 또는 에러가 반환될 때까지 기다린다.

async function fetchData() {
  try {
    const data = await fetch('url'); // 프로미스가 이행될 때까지 기다림
    const json = await data.json();
    console.log(json);
  } catch (error) {
    console.error(error);
  }
}

 

이러한 메커니즘을 통해 자바스크립트는 싱글 스레드임에도 불구하고 다수의 비동기 작업을 효과적으로 관리하고, 동시에 여러 작업을 수행할 수 있는 동시성을 제공한다.

728x90
728x90

React로 프론트엔드를 설계하고, Express.js서버로 간단한 CRUD 기능이 있는 커뮤니티를 제작하고 있다.

 

client와 server의 폴더를 완전히 분리시켜서 작업하였고, concurrently와 nodemon을 이용하여 개발을 하였다.

 

Backend의 Architecture는 이렇다.

MYUN_SJ backend/  
├── app.js  
├── package.json  
├── /config  
│   └── db.js  
├── /models  
│   ├── user.js  
│   └── post.js  
├── /routes  
│   ├── index.js  
│   ├── users.js  
│   └── posts.js  
├── /controllers  
│   ├── usersController.js  
│   └── postsController.js  
├── /middlewares  
│   └── authMiddleware.js  
├── /public  
│   ├── /images  
│   ├── /javascripts  
│   └── /stylesheets  
└── /views (RESTful API이기 때문에 제거)

 

지금은 개발단계이기 때문에 localhost로 진행하였고, public이나 index.js의 활용은 아직 하고있지 않는다.

 

완전히 분리하여 포트번호도 다르기 때문에, cors정책을 위반하게 된다. 이를 처리하는 로직을 express 서버의 main 호출단계에 추가해주었다. main단에서는 최대한 호출정도의 기능만 하기 위하여 최대한 압축시켰다.

const express = require('express');
const app = express();
const cors = require('cors');
const session = require('express-session');
require('dotenv').config();

const sequelize = require('./config/db');

const User = require('./models/user');
const Post = require('./models/post');

const postsRoutes = require('./routes/posts');
const usersRoutes = require('./routes/users');

app.use(cors());

app.use((req, res, next) => {
    res.header('Cache-Control', 'no-cache, no-store, must-revalidate');
    res.header('Pragma', 'no-cache');
    res.header('Expires', '0');
    next();
});

app.use(express.json());
app.use(express.urlencoded({ extended: true }));

app.use(session({
    secret: process.env.SESSION_KEY,
    resave: false,
    saveUninitialized: true,
    cookie: { secure: 'auto' }
}));

app.use('/api/posts', postsRoutes);
app.use('/api/users', usersRoutes);

const PORT = process.env.PORT || 5000;
app.listen(PORT, () => {
    console.log(`Server running on port ${PORT}`);
    sequelize.authenticate().then(() => {
        console.log('Database connected...');
        sequelize.sync().then(() => {
            console.log('Tables created...');
        }).catch(err => console.log('Error: ' + err));
    }).catch(err => console.log('Error: ' + err));
});

 

DB는 MySQL의 sequelize를 사용하여 편하게 설계하였다. GMT 시간을 맞추는 로직이 필요하였다.

require('dotenv').config();
const { Sequelize } = require('sequelize');

const sequelize = new Sequelize(process.env.DB_NAME, process.env.DB_USER, process.env.DB_PASSWORD, {
    host: process.env.DB_HOST,
    dialect: 'mysql',
    timezone: '+09:00'
});

sequelize.authenticate()
    .then(() => console.log('Connection has been established successfully.'))
    .catch(err => console.error('Unable to connect to the database:', err));

module.exports = sequelize;

 

프론트에서 최대한 요청을 하고, 백에서 최대한 처리를 하기 위하여 로그아웃 로직도 백에 추가시켜주었다. Node.js이기 때문에 적합한 Rest.API를 채택하여 통신을 하였다.

const User = require('../models/user');

const usersController = {
    // 모든 사용자 조회
    getAllUsers: async (req, res) => {
        try {
            const users = await User.findAll();
            res.json(users);
        } catch (error) {
            res.status(500).json({ message: error.message });
        }
    },

    // 사용자 생성
    createUser: async (req, res) => {
        try {
            const user = await User.create(req.body);
            res.status(201).json(user);
        } catch (error) {
            res.status(400).json({ message: error.message });
        }
    },

    // 특정 사용자 조회
    getUserById: async (req, res) => {
        try {
            const user = await User.findByPk(req.params.id);
            if (user) {
                res.json(user);
            } else {
                res.status(404).json({ message: '사용자를 찾을 수 없습니다.' });
            }
        } catch (error) {
            res.status(500).json({ message: error.message });
        }
    },

    // 사용자 업데이트
    updateUser: async (req, res) => {
        try {
            const user = await User.findByPk(req.params.id);
            if (user) {
                await user.update(req.body);
                res.json(user);
            } else {
                res.status(404).json({ message: '사용자를 찾을 수 없습니다.' });
            }
        } catch (error) {
            res.status(400).json({ message: error.message });
        }
    },

    // 사용자 삭제
    deleteUser: async (req, res) => {
        try {
            const user = await User.findByPk(req.params.id);
            if (user) {
                await user.destroy();
                res.status(204).send();
            } else {
                res.status(404).json({ message: '사용자를 찾을 수 없습니다.' });
            }
        } catch (error) {
            res.status(500).json({ message: error.message });
        }
    },

    // 사용자 로그인
    loginUser: async (req, res) => {
        const { email, pw } = req.body;
        try {
            const user = await User.findOne({ where: { email: email } });
            if (!user) {
                return res.status(404).json({ message: '사용자를 찾을 수 없습니다.' });
            }

            // 비밀번호 검증 로직 추가 일치 여부만 확인
            if (user.pw !== pw) {
                return res.status(400).json({ message: '잘못된 비밀번호입니다.' });
            }

            // 세션에 사용자 ID 저장
            req.session.userId = user.id;
            res.json({ message: '로그인 성공!' });
        } catch (error) {
            res.status(500).json({ message: error.message });
        }
    },

    // 사용자 로그아웃
    logoutUser: async (req, res) => {
        try {
            req.session.destroy((err) => {
                if (err) {
                    console.log(err);
                    res.status(500).json({ message: '로그아웃 에러' });
                } else {
                    res.json({ message: '로그아웃 성공' });
                }
            });
        } catch (error) {
            res.status(500).json({ message: error.message });
        }
    }
};

module.exports = usersController;

 

세션 방식을 이용하여 통신했기 때문에 bcrypt 등(아직 추가하지 않음) 여러 검증 로직이 필요했었다.

function authMiddleware(req, res, next) {
    if (req.session && req.session.userId) {
        next(); // 세션 정보가 유효하면 다음 미들웨어로 진행
    } else {
        res.status(401).send('Unauthorized'); // 로그인되지 않은 사용자에게 401 상태 코드 응답
    }
}

module.exports = authMiddleware;

 

https://github.com/MYun-SJ/MYun_SJ/tree/main/server

 

MYun_SJ/server at main · MYun-SJ/MYun_SJ

MYun_SJ. Contribute to MYun-SJ/MYun_SJ development by creating an account on GitHub.

github.com

 

728x90

'서버' 카테고리의 다른 글

Proxy 서버 구현  (1) 2024.03.24
echo server, tiny server 구현  (2) 2024.03.24
Socket, Redirection, Pipe  (1) 2024.03.23
Parsing, Caching, Filtering, Load Balancing, MTU, NAT  (1) 2024.03.23
728x90

 

 

지금 Azure 체험 계정 만들기 | Microsoft Azure

12개월 체험 서비스, 항상 무료인 40개 이상의 서비스 및 200 USD 크레딧으로 시작하세요. Microsoft Azure에서 지금 체험 계정을 만들어 보세요.

azure.microsoft.com

 

1. 리소스 만들기 -> 가상 머신 만들기

2. 우측에 가격 보면서 지역 바꿔가면서 최대한 싸게 측정하기 (다른 세팅 다 하고 나서 하는걸 추천)

3. 세팅이 끝났으면 검토 + 만들기

4. 해당 가상머신 -> 연결 -> 원시 SSH 선택 -> VM시작

 

https://mobaxterm.mobatek.net/

 

MobaXterm free Xserver and tabbed SSH client for Windows

The ultimate toolbox for remote computing - includes X server, enhanced SSH client and much more!

mobaxterm.mobatek.net

 

5. MobaXterm 실행 -> Session -> SSH -> 아까 만든 가상머신의 주소와 유저네임 입력

 

6. 배포할 파일들 github등으로 가상머신에 받아오고 필요 패키지들 설치 및 가상머신 운영체제에 맞게 코드 수정 후 실행

 

++삭제하기

1. 홈 -> 리소스 그룹 모든 리소스 체크 후 리소스 그룹 삭제

 

나머지 리소스 cloud-shell-storage-southeastasia과 NetworkWatcherRG도 안쓸 것 같다면 지워도 상관이 없다.

 

cloud-shell-storage-southeastasia리소스 그룹은 Azure Cloud Shell을 사용할 때 자동으로 생성된다. Cloud Shell은 Azure 관리를 위한 브라우저 기반의 쉘 환경이며, 사용자의 데이터를 저장하기 위해 Azure Storage 계정을 필요로 한다. cloud-shell-storage-southeastasia는 동남아시아 지역에 위치한 Cloud Shell 사용자의 데이터를 저장하는 데 사용되는 리소스 그룹이다.

 

NetworkWatcherRG리소스 그룹은 Azure Network Watcher 서비스와 관련이 있다. Network Watcher

는 네트워크 성능 모니터링, 진단, 로깅 등을 제공하여 Azure 가상 네트워크의 리소스를 관리하고 문제를 해결하는 데 도움을 준다. Azure는 네트워크 모니터링을 활성화할 때 자동으로 NetworkWatcherRG 리소스 그룹을 생성하여 관련 리소스를 관리한다.

 

추가로 Azure 서비스를 이용안하겠다 싶으면 구독도 취소해야 돈이 안빠져나간다.

 

728x90

'기타' 카테고리의 다른 글

백준 알고리즘 문제풀이 스트릭 100, tistory 글 안써지는 issue fix  (0) 2024.05.19
use CDN  (0) 2024.04.23
Azure AD, AWS IAM, ACM  (0) 2024.04.18
Primitives, TLS, SSL  (0) 2024.04.15
728x90

이번 포스팅에서 정의한 테이블 및 데이터:

CREATE DATABASE mydatabase;
CREATE USER 'myundb'@'localhost' IDENTIFIED BY 'password';
GRANT ALL PRIVILEGES ON mydatabase.* TO 'myundb'@'localhost';
FLUSH PRIVILEGES;
USE mydatabase;
CREATE TABLE user(
  id INT NOT NULL AUTO_INCREMENT,
  email VARCHAR(500) NOT NULL,
  name VARCHAR(500) NOT NULL,
  nickname VARCHAR(500) NOT NULL,
  pw VARCHAR(500) NOT NULL,
  newdate DATETIME DEFAULT CURRENT_TIMESTAMP,
  updatedate DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  CONSTRAINT user_pk PRIMARY KEY (id)
);

 

예시 데이터 집어넣기:

 

MySQL 데이터베이스에 저장된 데이터를 확인하는 방법은 여러 가지가 있다.

 

첫 번째 방법은, 명령 프롬프트 또는 터미널에서 MySQL 클라이언트를 시작하고

mysql -u your_username -p

사용할 데이터베이스를 선택한다.

USE your_database_name;

그리고 필요한 데이터를 조회한다. 예를 들어, users 테이블의 모든 데이터를 조회하려면

SELECT * FROM users;

이렇게 할 수 있다.

 

결과 화면:

비밀번호 해싱을 아직 구현안한 상태

두 번째 방법은 MySQL Workbench에서 확인하는 방법이다.

 

SELECT  * FROM mydatabase.users; 를 쿼리로 실행해도 되고, UI로도 가능하다.

스키마에서 조회하고자 하는 테이블을 우클릭하고 Select Rows - Limit 1000을 누르면 상위 1000개의 데이터 목록을 확인할 수 있다. 만약 테이블을 정의해도 Schema에 데이터베이스가 안뜨면, Schemas 부분에 우클릭을 한 뒤, Load Spatial Data를 눌러 최근 리스트들을 갱신해야 한다.

 

결과 화면:

MySQL 데이터 베이스 관리 도구인 phpMyAdmin을 사용하여 브라우저를 통해 데이터베이스에 접속하고, 테이블을 조회하거나 SQL 쿼리를 실행할 수도 있다.

 

https://www.phpmyadmin.net/

 

phpMyAdmin

Your download should start soon, if not please click here. Please verify the downloaded file Please take additional steps to verify that the file you have downloaded is not corrupted, you can verify it using the following methods: phpMyAdmin needs your con

www.phpmyadmin.net

 

728x90

'데이터 > SQL' 카테고리의 다른 글

MySQL 테이블, user 정의 및 PATH 설정  (0) 2024.04.08
DB Index(SQL, NO SQL), Error Code: 1064  (0) 2024.04.05

+ Recent posts