Deloitte의 프로젝트 인턴에 지원했었다.
컨설팅 회사인 만큼, 학벌도 많이 볼 것이라 생각했고 지원자도 많을 것 같아 크게 기대는 안했었다.
여기도 인턴 공고가 꽤 많이 올라오는 것 같던데.. 몇 번 자기소개서를 쓰다가 어차피 안 될 것 같다는 생각에 관 둔 적이 많았다.

지원 공고

 
그러다 처음으로 자기소개서를 완성해서 제출을 했었는데, 기대는 별로 안했었다.
특히 응시 자격 중 저 'Fluent in English'가 굉장히 마음에 걸렸다. (필자는 토익 800점대 중반에 오픽 IM1의 소유자로, 영어에 소질이 있는 편은 아니었다. 해외 경험도 거의 전무하다 싶이 하다.)
오픽은 기재하지 않고, 토익 점수만 기재해서 '나 토익 800점 대인데 거르려면 걸러라' 라는 생각으로 지원했다.
 
딜로이트 인턴은 어떤 공고든 자기소개서 문항이 똑같은 것 같은데..지원동기와 최종 커리어목표 두 가지 문항이었고, 모두 1000자였다. 경험 위주로 작성했었다.
 

서류 합격 메일

합격 메일을 받고 굉장히 신기했다. 
또한 놀랐던 것은, 마감일 바로 하루 뒤에 결과가 나왔던 것이다.
물리적으로 이력서를 전부 검수할 수 있는 시간인지 모르겠다.. 내 생각에는 지원서가 들어올 때마다 그때 그때 적당히 확인하고, 거의 마감 되자마자 합격 메일을 보내는 것이 아닌가 싶다.
 
추가로, 25일과 26일 모두 참석이 가능하다고 회신을 보냈는데.. 갑자기 내일 면접이 가능하냐고 물어왔다.
굉장히 인력이 급한 상황인 것 같았다. 그리고 바로 다음날 면접을 보게 되었다.
(인성검사는 정말 성격 관련 문항만 있었고, 형식적인 프로세스라는 느낌을 받았다.)
 

면접 안내 메일

 
회사 사옥이 아닌 공유 오피스에서 면접을 봤다. (프로젝트 때문에 공유 오피스에서 근무 중이라고 했다.)
면접 분위기 자체는 편하게 진행되었으나, 질문이 아주 쉬웠던 것은 아니었다.
자기소개서 기반 질문은 별로 없었으며, '이러한 경험을 해봤냐' 라는 질문이 주를 이루었다.
 
면접 질문으로는

  • 자기소개
  • 주변에서 본인을 어떻게 평가 하는지
  • 협업 경험
  • 업무 하다가 막히면 어떻게 대처하는지
  • HR 관련 경험이 있는지 (나는 Cloud 관련 경험은 자기소개에서 많이 어필해서 HR 경험 질문을 받은 것 같다. 만약 HR 관련 경험을 주로 어필했다면 Cloud 관련 경험을 물어보지 않았을까 싶다.)
  • 어떤 기업의 특정 상황을 가정한 뒤, 기업에게 HR Cloud를 사용하라고 설득해보라는 내용
  • 영어 수준이 어느정도 되는지
  • 마지막으로 Deloitte 혹은 Consulting과 관련하여 궁금한 것이 있는지

등등이 있었다.
 
컨설팅 회사 면접은 처음이었는데, 컨설팅 회사에서 주로 물어본다고 하는 Why Deloitte나 Why Consulting 같은 질문은 없었다. (인턴이라 그런 걸까?)
또한 영어 질문이 있을까 두려웠지만.. 영어 수준이 어떠냐고 물어볼 뿐 영어로 질문을 하거나 영어로 대답하라는 요구는 없었다. 
그런데 영어가 중요하긴 한 것 같았다. 영어 실력 관련 질문에서 영어 독해는 어느정도 가능하지만, 듣고 말하는 것, 작문에는 어려움이 있다고 솔직하게 대답했는데, 마지막에 주로 어떤 업무를 하냐고 물어보았을때 영어 회의를 듣고 회의록 작성하는 것이 중요하다고 하더라... 이때 떨어질 것을 직감했다. ^^
 

불합격 메일

 
 
결과는 일주일 안쪽으로 나왔던 것 같다.
인력이 굉장히 급해보였기에 처음엔 기대를 했지만... 면접 본 뒤 결과가 너무나도 예상이 갔었기 때문에 그다지 슬프진 않았다.
어찌 되었든 생각보다 컨설팅 인턴의 진입 문턱이 높은 것 같진 않으니, 해보고 싶은 분들이 있다면 겁 먹지 말고 적극적으로 지원해봐도 좋을 것 같다. (다만, 해당 프로젝트 관련 지식과 경험..그리고 영어실력(ㅠㅠ)은 합격을 위한 필수 요건인 듯하다.)
 
 
 
 

2025년 8월, 네이버 클라우드 인턴에 지원했었다.

 

지원공고

 

사실 해당 공고 이외에도, 네이버 인턴에 두 번 지원한 적이 있었으며 모두 서류에서 탈락했었다.

그럼에도 불구하고 세 번째 지원에서 서류 합격을 할 수 있었던 것을 보면 한 두번 서류에서 탈락했다고 일종의 블랙리스트...에 오르는 것은 아닌 것으로 보인다.

(꼭 여기서 인턴을 해보고 싶은 분이 있다면, 지원에 돈이 드는 것도 아니니 탈락을 너무 두려워하지 말고 적극적으로 지원해보라고 말씀드리고 싶다.)

 

자기소개서 문항은  1000자 짜리 세 개로, 분량이 꽤 있어서 쓰기 쉽지는 않았다.

[필수] 자기자신을 자유롭게 소개해 주세요. (1000자)
> 전공 관련 내용, 지원 동기에 대해 작성했다.

[필수] 지원부문과 관련된 경험(스터디, 프로젝트, 업무 등)이 있으면 서술하고, 그 경험에서 자신이 어떤 역할을 했었고, 결과로 어떤 지식이나 교훈을 얻었는지 설명해 주세요. (1000자)
> 리서치 인턴을 한 번 해 본 경험이 있어서, 그 경험에 대해 작성했다.

[필수] 본인이 보유한 Skill의 활용 정도를 모두 적어주세요. [작성 예시 : GIS 기술 및 툴 활용 역량, UX설계/기획 도구, 데이터 마이닝 관련 기술 역량 등 자유롭게 기술해주세요 ] (1000자)
> OA역량...을 작성했다. 이거 때문에 떨어질 줄 알았다.

[필수] 협업 시 중요하게 생각하는 것과 잘하는 것을 설명해주세요. (커뮤니케이션 스킬, 친화력 있는 성격, 전문성 등) (1000자)
> 인턴 당시 경험을 작성했다.

 

서류합격 메시지

 

사실 네이버 인턴의 경우 결과 발표 메시지만 보아도 합격 여부를 알 수 있다.

불합격의 경우 단순히 채용 결과가 발표 되었으니 메일을 확인하라고 메시지가 오지만, 합격에게는 Naver Careers(채용 홈페이지)의 메시지함을 확인하라고 문자가 오더라.

 

놀란 것은, 8월 2일에 지원을 완료했는데 8월 7일에 합격 메시지가 왔다.

지원자가 굉장히 많을 것 같은데... 일주일도 안되어서 서류 검토가 완료 되었다는게 놀라웠다. AI 필터링을 하는 걸까?

면접 준비의 흔적

 

이정도 규모 기업의 인턴에 서류 합격을 한 것은 처음이라, 신기하기도 해서 면접을 꽤 열심히 준비했었다.

크게 기업 분석, 최근 AI 기술 트렌드 조사, 경험 정리로 나누어 면접을 준비했다.

 

면접은 면접관 두 명과 피면접자 한 명으로 진행되었다.

 

기억에 남는 면접 질문으로는,

  • 자기소개
  • 최근 AI 트렌드
  • 어떤 업무를 할 것이라 생각하고 지원하였는지
  • 이번 인턴을 통해 어떤 것을 얻어가고 싶은지
  • IT 용어들 등 적응 빨리 한느 편인지
  • 네이버 LLM이 타사 LLM에 비해 가지는 장점
  • 네이버가 경쟁사들에 비해 차별화 되기 위하여 해야 할 것
  • 마지막으로 하고 싶은 말
  • 궁금한 점

이 있었으며, 이외에도 개인적인 경험에 대한 질문들도 많았다. (거의 30분 꽉 채워 진행되었었다.)

면접관 분께서 내가 이전에 했었던 인턴 업무가 네이버 인턴 업무와 굉장히 Fit 할 것 같다는 말씀을 하셔서, 합격할 줄 알았다. ^^

하반기 공채를 지원할 것인지에 대한 질문도 했었는데... 아마 여기서 대답을 제대로 못한게 패인이 아닐까 싶다.

 

 

너 탈락한거야

 

결과는 면접 본 지 일주일 정도 뒤에 나왔으며, 불합격이었다. (이제 메일 제목만 봐도 합불 여부를 알 수 있다!)

합격하면 대부분 3일 내로 결과가 나온다더라. 

 

면접날 비가 많이 와서 힘들었지만.... 그래도 좋은 경험이었다.

 

 

 

 

2024년 8월, '국디서'라 불리는 국민은행 디지털 서포터즈에 지원했었다.

 

지금은 은행에 지원하지 않고 있지만, 당시에는 은행 취업 생각이 있었기에...

지원자가 엄청나게 많다고 들어서 크게 기대는 하지 않았었다. 

그럼에도 불구하고 서류에 합격한 것은 지원자 못지 않게 모집인원 또한 엄청나게 많았기 때문..으로 생각한다.

지원 공고

 

현재는 자기소개서 문항이 바뀌었을 것이라 생각한다.

당시 자기소개서 문항은 세 가지였다.

 

1. 디지털 서포터즈에 지원하게 된 동기와, 디지털 서포처즈 근무 경험을 통해 얻고자 하는 것이 무엇인지 기술해주세요. (500자)

 

고객에게 서비스를 제공하는데서 보람을 느낀다고 썼었다. (서울 번화가 편의점 아르바이트를 9개월 했던 것을 언급했다.)

해당 프로그램이 은행에서 안내 업무를 주로 한다고 들어서, 서비스업 종사 경험을 좋게 볼 것이라 생각했다.

 

2. 디지털 서포터즈로 근무하기 위해 가장 필요한 핵심 역량 한 가지를 정의하고, 그렇게 생각한 이유와 그 역량을 갖춘 사람임을 구체적인 사례를 들어 설명해주세요. (500자)

 

성실성이 중요하다고 썼다. 사실 너무 뻔한 대답인데, 사례를 들어 설명하라고 해서.. 내 입장에서 사례 들어 설명할 수 있는 역량이 성실성밖에 없다고 생각했다.

그리고 국민은행 디지털 서포터즈가 업무 강도가 강해서, 중간에 그만두는 사람이 많다고 들어서 괜찮을 것 같다고 생각했다. 사례로 지금까지 모든 아르바이트를 6개월 이상 근무했고, 학점과 금융 자격증 등을 언급했었다.

 

3. 학교생활 / 대외활동 / 아르바이트 등의 활동 중, 다른 사람이 생각해내지 못했던 문제를 해결한 경험에 대해 기술해주세요. (500자)

 

교내 튜터링 경험을 썼다.

 

서류 전형 결과 발표

 

그리고 예상치 못하게 서류전형에 합격했다.

휴학을 해야 해서, 면접을 참석할지 말지 고민했다. 그런데 면접이 비대면이기도 하고, 우선 다 붙고 나면 그 때 고민하자는 생각으로 면접에 참석했다.

 

그런 것 치고는 면접 준비를 꽤 열심히 했는데, 당시 준비 했던 공통 문항들은 아래와 같다. 

(경험 기반 질문은 또 따로 준비했었다..)

  • 1분 자기소개
  • 지원동기
  • 모르는 것을 손님이 물어본다면 어떻게 대처할 것인지
  • 손님이 밀린 대기로 화가 났다면 어떻게 대처할 것인지
  • 진상 고객과 민원을 어떻게 응대할 것인지
  • 스트레스 해소 방법
  • 디지털 서비스를 거부하는 고객을 어떻게 응대할 것인지
  • 디지털 금융 서비스에서 중요한 것
  • 최근 국민은행을 방문했을 때 좋았던 점과 아쉬웠던 점
  • 해보고 싶은 활동
  • 관심 있는 국민은행 상품과 서비스
  • 디지털 서포터즈의 역할과 나의 역량
  • 기억에 남는 실패 경험
  • STM기에 대해 알고 있는지
  • 은행에서 디지털이 중요해지고 있는 이유
  • 본인의 장점과 단점
  • 해당 활동을 통해 배우고 싶은 점
  • 존경하는 인물

 

면접은 다대다로 진행되었으며, 면접관 2명과 피면접자 4명이었던 것으로 기억한다.

(오래 되어서 확실하지는 않다.)

 

실제 면접 질문은 공통질문 세 개, 그리고 개인 질문, 마지막으로 하고 싶은 말 정도로 구성되었던 것 같다.

  • 1분 자기소개
  • 스트레스를 어떻게 푸는지
  • 사람과 갈등 관련 질문 1개 (정확한 질문이 기억나지 않는다.)
  • 개인 질문
  • 마지막으로 하고 싶은 말

준비한 것에 비해 실제 질문은 크게 어렵지 않았었고....

최종적으로는 탈락했는데, 아무래도 비대면임에도 불구하고 내가 활발한 성격이 아닌 것이 면접관에게 느껴지지 않았나 싶다....

좋은 경험이었다.

 

1. IP Address (IPv4)

- 컴퓨터 장치들이 네트워크 상에서 서로를 인식하기 위해 사용하는 32bit 길이의 고유한 번호
 - 0.0.0.0 ~ 255. 255. 255. 255 (0000 0000. 0000 0000. 0000 0000. 0000 0000 ~ 1111 1111. 1111 1111. 1111 1111. 1111 1111) 까지 존재할 수 있다.
- 일부는 서브넷에 의하여, 일부는 호스트에 의하여 정해진다.

IP address의 class

- 네트워크상에 많은 IP주소를 사용해야 하면 Host Address 범위가 넓은 클래스를, 그렇지 않으면 Network 범위가 넓은 클래스를 사용

  • Class A: 0xxx xxxx. (1.0.0.0 - 126.0.0.0)
  • Class B: 10xx xxxx. (128.0.0.0 - 191.255.0.0)
  • Class C: 110x xxxx. (192.0.0.0 - 223.255.255.0)

- Class 마다 각각의 시작 숫자가 정해져있는 이유는 IP주소를 보고 편리하게 Class르 구분하기 위함
- 현재는 Class체계를 사용하지 않고 CIDR(Classless Inter-Domain Routing) 를 사용
 

2. NetMask

- IP에서 네트워크 주소와 호스트 주소를 분리하는마스크
- 네트워크 주소는 1로, 호스트 주소를 0으로 표기하여 IP주소와 AND 연산 하면 네트워크주소가 나온다.

2. SubnetMask

- 할당된 네트워크 주소를 다시 여러개의 작은 네트워크로 나누어 사용하는 것
- IP 주소 뒤에 "/x" 를 붙이는데, 이는 IP주소의 앞 x자리가 네트워크 주소라는 의미

  • 라우터가 어떤 네트워크로 패킷을 보내야 하는지 판별하기 쉬움.
  • 네트워크를 논리적으로 구분 할 수 있음.
  • IP주소를 효율적으로 관리 할 수 있음.

3. SubnetMask 계산 방법

ex ) 194. 139. 10. 7 / 25
- '194. 139. 10. 7' 는 '1100 0010. 1000 1011. 0000 1010. 0000 0111' 이다. (Class C IP주소로, 원래 마지막 뒤 8자리가 호스트 주소.)
- '/25'는 앞 25bit가 네트워크 주소로 사용된다는 의미이므로 '1100 0010. 1000 1011. 0000 1010 0000 0111' 로, 끝 7자가 호스트 주소가 되고, 네 번째 옥텟의 첫번째 비트가 Subnet number가 되었다.
- Subnet number는 네트워크 주소로 소속이 되고, Subnet Number가 0일때와 1일때로 네트워크 주소의 할당 자리는 2배가 되지만, 호스트 주소의 할당 자리는 1/2배가 된다.
- 첫 번째 Subnet 범위는 194. 139. 10. 0 ~ 194. 139. 10. 127, 두 번째 Subnet 범위는 194. 139. 10. 128 ~ 194. 139. 10. 255가 된다.
- Subnetting을 통해 하나의 큰 네트워클르 더 작은 서브넷으로 분리하면 각 서브넷은 자체적인 네트워크 주소와 IP 주소 범위를 가지고, 서로 다른 서브넷간에는 라우터를 통하여 통신한다.
 
4. Network Address& BroadCast Address
- Network Address : 특정 서브넷의 시작 주소로, 일반적으로 호스트에 할당되지 않으며 라우팅 테이블에서 경로를 지정할 때 사용
- Network Address는 호스트 부분의 모든 비트가 0인 주소로 표현된다.
- Broadcast Address: 특정 서브넷 내의 모든 호스트에 패킷을 전송하기 위한 주소로, 일반적으로 호스트에 할당되지 않는다.
- Broadcast Address는 호스트 부분의 모든 비트가 1인 주소로 표현된다.

1. perror / strerror

 

1) perror

<stdio.h>
void perror(const char *s);

- 입력받은 문자열과 errno에 대한 오류 메시지 결합하여 출력

- 오류가 발생하면 해당 오류 코드가 errno에 저장되는데 이를 가져와서 출력하는 것임

2) strerror

<string.h>
char *strerror(int errnum);

- 오류 코드를 매개변수로 받아 해당 오류코드에 대한 오류 메시지를 반환

- 문자열을 반환하므로  printf 함수와 함께 사용해야 함

2. access

#include <unistd.h>
int access(const char *path, int mod);

- 파일이나 디렉토리에 대한 접근 권한을 확인하는 데 사용

- 접근 권한 확인 후 성공하면 0, 실패하면 -1을 반환

- F_OK : 파일의 존재 여부 확인 / R_OK : 읽기 권한 확인 / W_OK : 쓰기 권한 확인 / X_OK : 실행 권한 확인

 

3. dup / dup2

int dup(int oldfd);

- oldfd로 지정된 파일 디스크립터를 복제하여 새로운 파일 디스크립터를 생성

- 성공시 새로운 파일 디스크립터를 반환하며, 실패시 -1을 반환

#include <unistd.h>
int dup2(int oldfd, int newfd);

- oldfd로 지정된 파일 디스크립터를 newfd로 지정된 파일 디스크립터로 복제

- newfd가 이미 열려 있는 경우, new_fd를 닫고 oldfd를 복제하여 newfd로 사용

- 위 경우 표준 출력의 내용이 output.txt에 기록

 

4. execve

#include <unistd.h>
int execve(const char * pathname, char *const argv[], char *const envp[]);

- 지정된 경로 (pathname)의 프로그램 파일을 실행하고, 실행 중인 프로세스의 이미지를 해당 프로그램으로 교체 한 뒤 argv와 envp의 내용을 새로운 프로그램에 전달

5. fork

#include <unistd.h>
pid_t fork(void);

- 기존 프로세스에서 새로운 자식 프로세스를 생성 (각 프로세스는 동일한 코드와 상태를 가지지만, 서로 다른 프로세스 ID(PID)를 가짐

- 자식 프로세스인 경우 0을 반환하며, 부모 프로세스인 경우 현재 프로세스가 복제되어 자식 프로세스가 생성

6. pipe

- 파이프는 두 개의 파일 디스크립터로 이루어진 단방향 통신 채널로, 한 쪽에서 쓰여진 데이터를 다른 쪽에서 읽을 수 있게 해 줌

#include <unistd.h>
int pipe(int pipefd[2]);

-pipe() 함수는 프로세스 간 통신을 위해 사용되며, 파이프를 통해 데이터를 안전하게 전송

- pipefd[0]은 읽기용 파일 디스크립터를 저장,  pipefd[1]은 쓰기용 파일 디스크립터를 저장

-pipe() 함수를 호출하면 파이프가 생성되고, 연결된 파일 디스크립터를 pipefd 배열에 저장

- 파이프는 프로세스간 통신 방법 중 하나로, 파이프는 읽기를 위한 파일 디스크립터와 쓰기를 위한 파일 디스크립터, 총 2개로 이루어짐

- 한 프로세스가 파이프의 쓰기용 파일 디스크립터를 통해 데이터를 파이프에 쓰면 다른 프로세스가 읽기용 파일 디스크립터를 통해 읽음

- fork() 함수가 호출되면 부모프로세스와 거의 동일한 자식 프로세스가 생성.

- fork()를 호출한 후에는 두 프로세스(부모와 자식)이 있으므로, 한 프로세스에서는 else if 블록이 실행되고, 다르 프로세스에서는 else 블록이 실행]

- 따라서 위의 코드를 실행하면 부모 프로세스가 "Hello, child!"라는 메시지를 자식 프로세스로 전달하고, 자식 프로세스는 해당 메시지를 받아 출력

 

7. unlink

- 파일 시스템에서 파일을 삭제하는데 사용되는 함수로, 해당 파이르이 디렉토리 엔트리를 제거하여 파일을 삭제

- 삭제 성공하면 0, 실패하면 -1.

#include <unistd.h>
int unlink(const char *pathname);

- 상대경로, 절대경로 둘 다 사용할 수 있음

 

8. wait / waitpid

1) wait

-부모 프로세스가 자식 프로세스가 종료될 때까지 기다리도록 하는 역할

#include <sys/types.h>
pid_t wait(int *status);

- status 매개변수를 통해 어떤 방식으로 자식 프로세스가 종료 되었는지를 부모 프로세스가 알 수 있음

- 종료한 자식 프로세스의 프로세스 pid를 반환

 

* 넘겨주었던 status를 아래와 같은 매크로에 넣어 종료 상태를 확인할 수 있음.

WIFEXITED(status) 자식 프로세스가 정상적으로 종료되었는지 확인. 정상 종료되었다면 이 매크로는 참 값을 반환
WEXITSTATUS(status) 자식 프로세스의 반환 값을 얻기 위해 사용. WIFEXITED(status)가 참일 때만 호출
WIFSIGNALED(status) 자식 프로세스가 시그널에 의해 종료되었는지 확인하는데 사용
WTERMSIG(status) 자식 프로세스를 종료시킨 시그널의 번호를 반환. WIFSIGNALED(status)가 참일때만 호출
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main() {
    pid_t pid = fork();
    
    if (pid == -1) {
        perror("fork failed");
        return 1;
    } else if (pid == 0) {
        // This is the child process.
        printf("I'm the child process.\n");
        exit(7);
    } else {
        // This is the parent process.
        printf("I'm the parent process.\n");
        
        int status;
        pid_t child_pid = wait(&status);
        
        if (WIFEXITED(status)) {
            printf("Child %d ended normally. Exit status: %d\n", child_pid, WEXITSTATUS(status));
        } else {
            printf("Child %d ended abnormally.\n", child_pid);
        }
    }
    
    return 0;
}

 

2) waitpid

- 자식 프로세스의 상태 변화를 기다리는데 사용되며, wait() 함수보다 더 풍부한 기능을 제공

pid_t waitpid(pid_t pid, int *status, int options);
pid 대기하려는
자식 프로세스의 ID를
지정
pid > 0 프로세스 ID가 pid인 자식 프로세스를 기다림
pid = 0 호출자와 같은 프로세스 그룹 ID를 가진 모든 자식 프로세스를 기다림
pid = -1 모든 자식 프로세스를 기다림
pid < -1 프로세스 그룹 ID가 pid인 절대 값인 자식 프로세스를 기다림
status 자식 프로세스의 종료 상태를 저장하는 포인터
options 옵션을 설정하는 플래그 WNOHANG 상태 변경을 감지할 수 있는 자식 프로세스가 존재하지 않을 경우 즉시 0을 반환
WUNTRACED 자식 프로세스가 중지된 경우에도 (일시 정지와 같은 상황) waitpid()를 반환하여 부모 프로세스가 자식 프로세스의 일시 정지 상태를 알 수 있음

 

1.  가상머신이란?

1) 정의 및 작동 방식

가상머신 : 물리적 컴퓨터와 동일한 기능을 제공하는 소프트웨어 컴퓨터

가상화의 발전과정

- 기본적으로 컴퓨터는 하드웨어 위에 운영체제가 올라가있고, 그 위에서 응용 프로그램이 작동하는 방식으로 동작. 이 때 메모리에 상주하는 운영체제의 핵심 부분을 커널이라고 한다.

- 가상머신은 하나의 물리적인 컴퓨터에서 여러개의 독립적인 가상환경을 생성하여 동작시키는 기술

-  하이퍼바이저는 가상머신의 생성, 자원 할당, 네트워킹, 저장장치 관리, 보안 등을 처리하며, 가상머신들 간의 리소스 분할과 격리를 관리하여 각각의 가상머신이 독립적으로 동작 할 수 있도록 한다.

- 도커는 컨테이너 기술을 사용하여 호스트 시스템의 운영체제를 공유하기 때문에 더 가볍고 효율적인 리소스 사용이 가능

 

2) 목적

- 하나의 컴퓨터로 동시에 서로 다른 2개 이상의 운영체제를 실행하고 싶을 때

- 하나의 컴퓨터의 자원을 여러 명에게 나누어주고자 하는데 각 사용자 간 상호 간섭을 없애고 싶을 때

- 컴퓨터의 다른 영역에 영향을 주지 않는 독립적인 환경을 만들고 싶을 때

- 각 가상머신은 다른 가상머신과 격리되어 있으며, 서로의 운영체제와 애플리케이션에 영향을 주지 않는다. 또한 물리적인 하드웨어와 독립적이므로 다른 호스트 컴퓨터로 이동하거나 클라우드 환경에서 실행될 수 있다.

 

2. 파티션과 LVM(Logical Volume Manager)

파티션 : 물리적인 디스크를 여러 논리적인 섹션으로 분할하는 것. 일반적으로 운영체제, 응용 프로그램, 데이터 등을 구별하기 위해 사용

LVM : 물리적인 디스크를 논리적인 단위로 관리하는 프레임워크. 논리적인 디스크 공간을 필요에 따라 동적으로 변경하거나 재분배 할 수 있다.

파티션이 디스크를 분할하는데 초점을 맞춘 기법이라면, LVM은 디스크를 분할하고 합치고 크기를 동적으로 조정하는 등 더욱 유연한 디스크 관리를 가능하게 한다.

 

가상머신에서 파티션 분할

리눅스 시스템에서는 하나의 하드디스크에 최대 4개의 기본 파티션을 생성할 수 있으며, 필요에 따라 더 많은 파티션을 생성하고 싶을 때 확장 파티션을 생성한다. 즉, 4개 이상의 파티션이 필요하면 하나의 파티션을 확장 파티션으로 잡고 확장 파티션 내에서 다시 논리 파티션을 구분하는 방식으로 우회하는 것이다. 

위 사진은 가상머신에서 리눅스 파일 시스템 계층 구조를 간략하게 구현한 것이다.

/ 루트 디렉토리로, 시스템이 시작되는 첫 번째 위치
/boot 리눅스 시스템을 부팅하는데 필요한 파일들이 저장되어 있는 디렉토리
[SWAP] 컴퓨터의  RAM이 가득 찼을 때, 리눅스 시스템이 임시로 사용하는 디스크 공간
/home 일반 사용자들의 홈 디렉토리를 담고 있다. 사용자를 등록하면 일반적으로 /home/계정명으로 사용자 계정 디렉토리가 생성되고 운영된다.
/var 시스템이 실행되면서 내용이 자주 변경될 수 있는 데이터를 저장 (로그 - 시스템 상태, 동작 등을 기록 / 스풀 - 프린터 작업, 메일, 스케줄 작업 등)
/srv 시스템에서 제공하는 서비스 데이터를 저장
/tmp 시스템이 재부팅 될 때 삭제되는 일시적인 파일들을 저장

 

3.Sudo

- 'super user do'의 약자로, 각종 명령어 앞에 sudo를 붙이면 최고 관리자 권한으로 실행된다.

- etc/sudoer 설정 파일에 명시된 사용자만 사용 가능하다.

비밀번호 정책설정

- Deafult env_reset : sudo를 사용할 때 사용자가 시스템의 환경변수만 사용되게 하도록 제한. 환경변수가 예기치 않게 변경되어 의도하지 않은 동작을 유발 하지 않도록 함.

- mail_badpass : 비밀번호를 잘못 입력한 경우, 이 설정에 따라 알림 이메일이 관리자에게 전송.

- secure_path : sudo를 사용하여 실행할 수 있는 명령어의 경로를 제한. 이를 통해 sudo를 사용하는 사용자가 임의의 명령어를 실행하는 것을 방지.

- authfail_message : 비밀번호 인증에 실패한 경우에 표시되는 메시지를 지정. %d는 실패한 비밀번호 시도 횟수를 나타내는 특수한 플레이스 홀더.

- badpass_message : 잘못된 비밀번호를 입력했을 때 나타나는 오류 메시지.

- log input/log output : sudo를 사용하여 실행한 명령어의 입력과 출력을 로그 파일에 기록

- requiretty : sudo를 사용하여 실행되는 명령이 tty(터미널)가 필요로 하는지를 지정

- iolog_dir : sudo 사용 로그파일의 디렉터리 경로를 지정

- passwd_tries : 사용자가 비밀번호를 재시도 할 수 있는 최대 횟수를 지정

권한 설정

해당 항목은 사용자별로 sudo를 사용할 수 있는 특권을 지정하는 부분으로, 각 줄의 구조는 "사용자명 호스트 = (실행할 사용자 : 실행할 그룹) 실행할 명령"을 의미한다.

따라서 위의 경우 모든 호스트(컴퓨터)에서 모든 사용자/모든 그룹으로 어떤 명령이든 실행 할 수 있도록 권한을 부여한 것이다.

sudo 그룹에 특정 사용자 추가하기

특정 사용자에게 sudo 권한을 부여하려면 '/etc/sudoers' 파일에서 권한 부여를 해주고, sudo group에 추가까지 마쳐야 한다.'

 

4. UFW (Uncomplicated Fire Wall)

- iptable 기반의 간단하고 사용하기 쉬운 방화벽 설정 도구

- 특정 포트, IP 주소 또는 서비스에 대한 접근을 허용하거나 거부하는 규칙을 설정할 수 있음

- 허용되거나 거부된 트래픽에 대한 로그를 기록

ufw를 통해 특정 포트만 열어 둘 수 있다.

- 포트는 컴퓨터와 외부 네트워크간의 통신을 위한 창구로 작동하므로, 특정 포트만 허용함으로써 악의적인 사용자들이 해당 포트를 통해 시스템에 엑세스하거나 공격하는 것을 방지 할 수 있음.

- 트래픽 제어 가능 (예컨대 웹 서버를 운영하는 경우 80번 포트만 허용하여 웹 트래픽을 처리하고, 다른 포트는 차단함으로써 비웹 트래픽을 제한).

 

5. SSH (Secure Shell)

- 네트워크상에서 안전한 원격 접속을 제공하는 프로토콜

- ssh는 데이터 통신을 암호화해서 보호하며, 무결성을 위해 암호화화 함께 메시지 무결성 체크를 사용, 공개키 기반 인증을 사용.

- ssh 나오기 전 사용하기 전 사용하던 telnet의 경우 평문으로 통신하며 무결성 체크를 지원하지 않고, 패스워드 인증을 사용.

/etc/ssh/sshd_config

- ssh 포트를 22번에서 4242번으로 변경 : 많은 공격자들이 기본적으로 사용되는 22번 포트를 대상으로 공격을 시도하므로, 포트 번호를 변경함으로써 보안을 강화

- ssh를 통해 root 권한으로 직접 접속하지 못하게 제한 : root는 시스템에서 최고 관리자 권한을 가지고 있으므로 일반 사용자에 대한 공격보다 훨씬 위험하기 때문에 root로의 직접적인 ssh 접속을 차단

 

6. 비밀번호 정책설정

/etc/login.defs

- 암호는 30일마다 만료

- 비밀번호를 수정하기 전에 허용되는 최소 일 수는 2

- 암호가 만료되기 7일 전에 경고 메시지를 전송

/etc/pam.d/common-password

- 최대 시도 횟수 : 3회

- 최소 길이 : 10

- 기존 암호와 달라야 하는 문자의 개수 : 7

- 대문자 1개 이상 포함, 소문자 1개 이상 포함, 숫자 1개 이상 포함

- 비밀번호에 유저명이 포함되어서는 안 됨

- 루트 계정에도 해당 규칙 적용

- 같은 문자가 3번 이상 반복되면 안 됨

 

* pam_pwquality

- pam(Pluggable Authentication Modules) : Linux 시스템에서 인증과 관련된 작업을 처리하는데 사용되는 프레임워크

- libpam -pwquality :  PAM을 확장하여 패스워드의 품질과 보안을 강화하는데 사용되는 모듈

pam을 사용하기 이전 리눅스 시스템에서는 각 응용 프로그램이 사용자 인증을 위해 자체적으로 로직을 구현하여 사용했기 때문에, 사용자 정보가 담긴 주요 시스템 파일인 '/etc/passwd'에 대한 접근 권한을 가지고 있어야 했다. 그러나 응용 프로그램이 사용자 정보가 담긴 주요 시스템 파일에 대한 직접적인 접근 권한을 가지고 있을 경우 보안과 관련한 위협이 있기 떄문에 pam이 등장하게 되었다.  pam을 사용하면 인증이 필요한 응용 프로그램은 더 이상 passwd 파일을 열람하지 않고 pam 모듈에 사용자 인증을 요청하며, pam은 요청한 사용자의 정보를 가지고 결과를 도출하여 응용 프로그램에 전달한다.

 

7. CRON

Unix 및 Unix 기반 시스템에서 주기적인 작업을 예약하기 위해 사용되는 시간 기반 작업 스케줄러. 'crontab' 파일을 사용하여 작업을 정의하며, 정의된 작업은 지정된 시간 간격에 따라 실행 된다.

crontab -e

- crontab -e 명령어는 crontab 파일을 편집하기 위해 사용되는 명령어.

- crontab -e 는 현재 사용자의 개인적인 crontab 파일을 편집하며, /etc/crontab 파일은 시스템 전체의 cron 작업을 관리.

- 위 경우 10분마다 /home 디렉토리의 monitoring.sh 파일을 출력.

 

1. Heap이란?

힙 (최대힙)

  • 힙은 우선순위 큐의 대표적인 자료구조이다.
  • 우선순위큐란 원소에 우선순위가 가장 큰 원소부터 접근이 가능한 자료구조이다.
  • 힙의 대표적인 종류로는 최대힙, 최소힙이 있으며 최대힙은 키값이 큰 원소가 높은 우선순위를, 최소힙은 키값이 작은 원소가 높은 우선순위를 가진다.
  • 최대힙 - 부모 노드의 원소가 자식 노드의 원소보다 크거나 같은 완전 이진트리
  • 최소힙 - 부모 노드의 원소가 자식 노드의 원소보다 작거나 같은 완전 이진트리
  • 힙에서 삽입 및 삭제의 시간 복잡도는 O(log2n)이다.

2. (최대)Heap 구현하기

구현할 내용은 아래와 같다.

  • heapifyUp(int i) - i번째 인덱스의 값을 부모 노드와 비교하여, 부모 노드의 값이 더 작으면 자리를 교환한다.
  • heapifyDwon(int i) - i번째 인덱스의 값을 자식 노드 두 개와 비교하여, 둘 중 큰 자식노드보다 i번째 인덱스 값이 더 작으면 자리를 교환한다.
  • insert(E x) - 힙의 적절한 자리에 입력받은 값 x를 입력한다.
  • delete() - 루트 노드에 있는 값을 삭제한다.
  • max() - 루트 노드에 있는 값을 리턴한다.
  • isEmpty() - 힙이 비어있으면 true, 그렇지 않으면 false를 리턴한다.
  • clear() - 힙의 모든 내용물을 비운다.
  • printAll() - 힙의 모든 내용물을 출력한다.

** 원래 힙에서는 맨 앞의 값에만 접근이 가능하나, 테스트 편의상 printAll() 메서드를 만들어 사용하였다.

 

1) 인터페이스

public interface heapInterface<E> {
	
	public void insert (E x);
	
	public void delete();
	
	public E max();
	
	public boolean isEmpty();
	
	public void clear();
	
	public void printAll();
	
}

 

2) 클래스

- 필드 영역

    //필드 영역
	private E[] item;
	private int size;
  • item[] - 원소를 저장한다.
  • size - 배열에 저장된 원소의 개수를 저장한다.

- 생성자 영역

	//생성자 영역
	public heap(int arraySize) {
		item = (E[]) new Comparable [arraySize];
		size=0;
	}
  • 배열의 크기를 입력받아 해당 크기의 배열을 생성한다. size변수는 0으로 초기화한다.

- 메서드 영역

<heapifyUp 메서드 구현>

	private void heapifyUp(int i) {
		
		if (i<0 || i>=item.length) {
			throw new IllegalArgumentException();
		} else {
		int parent = (i-1)/2;
		
		if (((Comparable) item[parent]).compareTo(item[i])<0) {
			E tmp = item[i];
			item[i] = item[parent];
			item[parent] = tmp;
			
		heapifyUp(parent);
			}
		}
	}
  • parent 변수에 입력받은 인덱스 노드의 부모 인덱스를 저장한다.
  • 만약 입력받은 인덱스 노드의 값보다 부모 노드의 값이 작으면 값을 바꾼다.
  • 부모 노드와 인덱스 노드의 값이 같거나, 부모 노드 값이 더 클 때까지 부모 노드에 대하여 해당 과정을 반복한다.
  • 만약 입력 받은 인덱스가 범위를 벗어났다면 에러를 리턴한다.

<heapifyDown 메서드 구현>

	private void heapifyDown(int i) {
		
		int left = 2*i+1;
		int right = 2*i+2;
		int max = i;
		if (i<0 || i>item.length) {
			throw new IllegalArgumentException();
		} else {
			if (left <=size-1) {
		if (((Comparable) item[max]).compareTo(item[left])<0) {
			max = left;
		} 
		if (((Comparable) item[max]).compareTo(item[right])<0) {
			max = right;
		}
		
		if (max!=i) {
			E tmp = item[i];
			item[i] = item[max];
			item[max] = tmp;
			
			heapifyDown(max); }
			}
		}
	}
  • left 변수에 입력받은 인덱스 노드의 왼쪽 자식 노드 인덱스, right 변수에 오른쪽 자식 노드 인덱스를 저장한다.
  • max 변수에 입력받은 인덱스를 저장한다.
  • left 변수가 범위를 벗어나지 않는 동안 아래 과정을 반복한다.
  • max 인덱스의 값과 left, right 인덱스 값을 비교하여 max 인덱스에 가장 큰 값의 인덱스가 들어오게 한다.
  • max의 값이 입력받은 값과 다르면(자식 노드중에 더 큰 값이 있다면) 입력 받은 인덱스 노드와 max 인덱스 노드의 자리를 바꾼다.
  • 입력받은 인덱스가 범위를 벗어났다면 에러를 리턴한다.

<insert 메서드 구현>

	@Override
	public void insert(E x) {
		if (size==item.length) {
			throw new IllegalArgumentException();
		} else {
		item[size]=x;
		heapifyUp(size);
		size++;
		}
	}
  • 힙의 끝에 입력받은 값을 추가한다.
  • heapifyUp을 통해 해당 노드가 적절한 자리를 찾게 한다.
  • size 변수를 1 증가시킨다. 
  • 만약 힙에 빈 자리가 없었다면 에러를 리턴한다.

<delete 메서드 구현>

	@Override
	public void delete() {
		if (isEmpty()) {
			throw new IllegalArgumentException();
		} else {
		item[0]=item[size-1];
		size--;
		heapifyDown(0);
		}
	}
  • 힙의 맨 끝자리에 있던 노드를 루트 노드 자리로 옮긴다.
  • size 변수를 1 감소시킨다.
  • heapifyDown을 통해 루트 노드 자리의 노드가 적절한 자리를 찾게 한다.
  • 만약 힙이 비어 있었다면 에러를 리턴한다.

<max 메서드 구현>

	@Override
	public E max() {
		if (isEmpty()) {
			throw new IllegalArgumentException();
		} else {
		return item[0];
			}
		}
  • 힙이 비어 있다면 에러를 리턴하고, 그렇지 않다면 루트 노드의 값을 리턴한다.

<isEmpty 메서드 구현>

	@Override
	public boolean isEmpty() {
		return (size==0);
	}
  • size 변수가 0인지 여부를 리턴한다.

<clear 메서드 구현>

	@Override
	public void clear() {
		item = (E[]) new Comparable [size];
		size=0;
	}
  • item 배열과 size 변수를 초기화한다.

<printAll 메서드 구현>

	@Override
	public void printAll() {
		for (int i=0; i<size; i++) {
			System.out.print(item[i]+" ");
		}
  • 인덱스 0부터 배열 끝까지 순회하며 해당 인덱스의 값을 출력한다.

-전체 코드

public class heap<E> implements heapInterface<E> {
	
	//필드 영역
	private E[] item;
	private int size;
	
	//생성자 영역
	public heap(int arraySize) {
		item = (E[]) new Comparable [arraySize];
		size=0;
	}
	
	//메서드 영역
	
	private void heapifyUp(int i) {
		
		if (i<0 || i>=item.length) {
			throw new IllegalArgumentException();
		} else {
		int parent = (i-1)/2;
		
		if (((Comparable) item[parent]).compareTo(item[i])<0) {
			E tmp = item[i];
			item[i] = item[parent];
			item[parent] = tmp;
			
		heapifyUp(parent);
			}
		}
	}
	
	private void heapifyDown(int i) {
		
		int left = 2*i+1;
		int right = 2*i+2;
		int max = i;
		if (i<0 || i>item.length) {
			throw new IllegalArgumentException();
		} else {
			if (left <=size-1) {
		if (((Comparable) item[max]).compareTo(item[left])<0) {
			max = left;
		} 
		if (((Comparable) item[max]).compareTo(item[right])<0) {
			max = right;
		}
		
		if (max!=i) {
			E tmp = item[i];
			item[i] = item[max];
			item[max] = tmp;
			
			heapifyDown(max); }
			}
		}
	}
	
	@Override
	public void insert(E x) {
		if (size==item.length) {
			throw new IllegalArgumentException();
		} else {
		item[size]=x;
		heapifyUp(size);
		size++;
		}
	}

	@Override
	public void delete() {
		if (isEmpty()) {
			throw new IllegalArgumentException();
		} else {
		item[0]=item[size-1];
		size--;
		heapifyDown(0);
		}
	}

	@Override
	public E max() {
		if (isEmpty()) {
			throw new IllegalArgumentException();
		} else {
		return item[0];
			}
		}

	@Override
	public boolean isEmpty() {
		return (size==0);
	}

	@Override
	public void clear() {
		item = (E[]) new Comparable [size];
		size=0;
	}

	@Override
	public void printAll() {
		for (int i=0; i<size; i++) {
			System.out.print(item[i]+" ");
		}
		
	}

}

 

3) 결과 확인하기

- 코드

public class Test {
	public static void main(String[] args) {
	
		heap<Integer> lst = new heap<>(64);
		
		lst.insert(100);
		
		lst.insert(300);
		
		lst.insert(400);
		
		lst.insert(200);
		
		lst.printAll();
		
		System.out.println();
		
		//100,200,300,400을 차례로 입력하고 출력합니다.
		
		lst.delete();
		
		lst.printAll();
		
		System.out.println();
		
		lst.delete();
		
		lst.printAll();
		
		System.out.println();
		
		//루트에 해당하는 값을 삭제하고 출력합니다.
		
		System.out.println(lst.isEmpty());
		
		System.out.println(lst.max());
		
		//lst가 비었는지 여부와 현재 루트에 있는 값을 출력합니다.
		
		
	}
}

- 결과

1. Queue란?

  • 먼저 들어온 데이터가 먼저 나간다. 즉, 선입선출(FIFO) 방식이다.
  • 맨 앞의 원소에만 접근이 가능하다. 

cf) 연결리스트로 큐 구현하기

연결 리스트로 큐를 구현할 때는 tail에 큐의 마지막 노드를 저장하고 tail이 next로 큐의 첫 번째 값을 참조하게 한다.

 

2. Queue 구현하기

구현할 내용은 아래와 같다.

  • enqueue(E x) - 원소 x를 큐의 끝에 추가한다.
  • dequeue() - 큐의 가장 앞에 있는 원소를 삭제한다.
  • front() - 큐의 가장 앞에 있는 원소를  리턴한다.
  • isEmpty() - 큐가 비어있으면 true, 그렇지 않으면 false를 리턴한다.
  • dequeueAll() - 큐에 있는 모든 원소를 삭제한다.
  • printAll() - 큐의 모든 원소를 순서대로 출력한다.

** 원래 큐에서는 맨 앞의 값에만 접근이 가능하나, 테스트 편의상 printAll() 메서드를 만들어 사용하였다.

 

1) 노드 클래스

public class Node<E> {

	public E item;
	public Node<E> next;
	
	public Node (E newItem) {
		item = newItem;
		next = null;
	}
	
	public Node (E newItem, Node<E> nextNode) {
		item = newItem;
		next = nextNode;
	}
}
  • item 변수에 데이터를 저장한다.
  • next 변수에 다음 노드를 저장한다. next는 다음 노드의 위치를 가리키는 포인터가 된다.
  • 첫 번째 생성자는 데이터를 입력받아 데이터를 item에 저장하고, 포인터 값은 null로 저장한다.
  • 두 번째 생성자는 데이터와 다음 노드를 입력받아 데이터를 item에 저장하고, 포인터 값에 다음 노드를 저장한다.

2) 인터페이스

public interface QueueInterface<E> {

	public void enqueue(E x);
	
	public void dequeue();
	
	public E front();
	
	public boolean isEmpty();
	
	public void dequeueAll();
	
	public void printAll();
	
}

 

3) LinkedQueue 클래스

- 필드 & 생성자 영역

	//필드 영역
	private Node<E> tail;
	int size=0;
	
	//생성자 영역
	public LinkedQueue() {
		tail=null;
	}
  • tail - 큐의 가장 끝 노드를 저장한다. (최근에 들어온 값)
  • size - 큐 원소의 개수를 저장한다.
  • 생성자 - tail에 null 값을 할당한다.

** 사실 위 메서드 중 printAll을 구현하지 않는다면 size는 필요하지 않다.

- 메서드 영역

<enqueue 메서드 구현>

	@Override
	public void enqueue(E x) {
		Node<E> newNode = new Node<>(x);
		if (isEmpty()) {
			tail = newNode;
			tail.next=tail;
		} else {
			newNode.next=tail.next;
			tail.next=newNode;
			tail=newNode;
		}
		size++;
	}
  • 입력 받은 값 x를 가지는 새 노드를 생성한다.
  • 만약 첫 노드라면 tail에 newNode를 입력하고 tail이 next로 스스로를 참조하게 한다.
  • 그렇지 않다면 새 노드가 front를 참조하고, tail이 새 노드를 참조하게 한다.
  • tail 값에 newNode를 입력한다.

<dequeue 메서드 구현>

	@Override
	public void dequeue() {
		if (isEmpty()) {
			System.out.println("ERROR");
		} else {
			if (tail.next==tail) {
				tail = null; 
			} else {
				tail.next=tail.next.next;
			}
		}
		size--;
	}
  • 만약 큐가 비어 있다면 에러를 출력한다.
  • 만약 큐에 원소가 한 개라면 tail 값에 null을 입력한다.
  • 큐에 원소가 여러 개라면 tail이 next로 기존 front값을 생략하고 그 다음 노드를 참조하게 한다.

<front 메서드 구현>

	@Override
	public E front() {
		if (isEmpty()) {
			return null;
		} else {
		return tail.next.item;
		}
	}
  • 만약 큐가 비어 있다면 null을 리턴한다.
  • 그렇지 않다면 front의 item값을 출력한다.

<isEmpty 메서드 구현>

	@Override
	public boolean isEmpty() {
		return (tail==null);
	}
  • tail==null 이면 true, 아니면 false를 리턴한다.

<dequeueAll 메서드 구현>

	@Override
	public void dequeueAll() {
		tail=null;
		size=0;
	}
  • tail과 size를 초기화한다.

<printAll 메서드 구현>

	@Override
	public void printAll() {
		Node<E> currNode=tail;
		for (int i=0; i<size; i++) {
			currNode=currNode.next;
			System.out.print(currNode.item+" ");
		}
	}
  • front부터 size만큼 큐를 순회하며 해당 노드의 item값을 출력한다.

- 전체 코드

public class LinkedQueue<E> implements QueueInterface<E> {

	//필드 영역
	private Node<E> tail;
	int size=0;
	
	//생성자 영역
	public LinkedQueue() {
		tail=null;
	}
	
	//메서드 영역
	
	@Override
	public void enqueue(E x) {
		Node<E> newNode = new Node<>(x);
		if (isEmpty()) {
			tail = newNode;
			tail.next=tail;
		} else {
			newNode.next=tail.next;
			tail.next=newNode;
			tail=newNode;
		}
		size++;
	}

	@Override
	public void dequeue() {
		if (isEmpty()) {
			System.out.println("ERROR");
		} else {
			if (tail.next==tail) {
				tail = null; 
			} else {
				tail.next=tail.next.next;
			}
		}
		size--;
	}

	@Override
	public E front() {
		if (isEmpty()) {
			return null;
		} else {
		return tail.next.item;
		}
	}

	@Override
	public boolean isEmpty() {
		return (tail==null);
	}

	@Override
	public void dequeueAll() {
		tail=null;
		size=0;
		
	}
	
	@Override
	public void printAll() {
		Node<E> currNode=tail;
		for (int i=0; i<size; i++) {
			currNode=currNode.next;
			System.out.print(currNode.item+" ");
		}
	}

}

 

3) 결과 확인하기

<코드>

public class Test {
	public static void main(String[] args) {
		
	
	LinkedQueue<Integer> lst = new LinkedQueue<>();
	
	lst.enqueue(100);
	
	lst.enqueue(200);
	
	lst.enqueue(300);
	
	lst.enqueue(400);

	lst.printAll();
	System.out.println();

	//LinkedQueue에 100,200,300,400을 순서대로 입력 후 현재 ArrayQueue 상태를 출력
	
	System.out.println(lst.front());
	
	//LinkedQueue의 가장 앞에 있는 값을 출력
	
	lst.dequeue();
	
	lst.printAll();
	
	System.out.println();
	
	//LinkedQueue에 100,200,300,400을 순서대로 입력 후 현재 ArrayQueue 상태를 출력
	
	System.out.println(lst.isEmpty());
	
	//LinkedQueue가 비었는지 출력
	
	
	}
}

<결과>

+ Recent posts