참고자료)이화여대 반효경 선생님 KOCW 강의


 

 

프로세스: 실행중인 프로그램이다.(윈도우 작업관리자 열면 볼 수 있는 것들!)

 

 

 

 

 우리가 C하드(보조기억장치-하드,시디)에 저장하듯 하드디스크에 저장되어 있고,

그걸 실행하는 작업대가 ram(주기억장치-램,롬)이다. 컴퓨터의 메모리라고 하면 일반적으로 램을 말한다.

바로 프로그램이 램에서 작업이 되는건 아니고 커널에 의해 레디큐로 갈지 말지 결정해서 레디큐로 갔다가,(이 과정은 커널의 데이터영역에서 이루어진다.그리고 이 커널의 주소공간 또한 램에 있다! https://whereisusb.tistory.com/10 참고, 헷갈렸던거 제대로 나와있음) 디스패쳐를 통해 램에 올라가 cpu에게 처리받는다. 

 

운영체제가 여러 프로그램을 실행하기 위해 자원(cpu,메모리,네트워크)을 분배한다.  

 

 

How??일단 기준보다 방법을 먼저 보자!

초록점이 프로세스 하나다.

CPU가 빠른 속도로 프로세스를 처리하고 있을 때 (해당프로세스상태:running)

정해진 시간이 끝나면(라운드로빈!) 큐의 뒤로 빠지고(해당프로세스상태:ready) 큐의 앞에 있던 프로세스가 시작된다.

혹은, 실행중인 프로세스가 IO처리가 필요하면 블록큐로 넘어가 끝날때까지 대기하게 된다.(해당프로세스상태:blocked)

IO에서 완료되면 cpu에 IO인터럽트를 해서 끝났다고 알려주고 CPU커널(깜빡깜빡!)을 통해 운영체제에게 아까 프로세스의 상태를 ready으로 바꿔달라고 요청한다. 우선적으로 처리할 수 있도록!

 

+프로세스의 상태(프로세스 스케줄링)

더보기

-running: CPU를 잡고 instruction을 수행중인 상태 

-ready: (물리적으로 코드가 메모리에 올려져있는 둥 모든 조건 만족하고)CPU를 기다리는 상태

-blocked: CPU를 주어도 당장 instruction을 수행할 수 없는 상태(디스크에서 file을 읽어와야하는 경우)

 ex) 어떤 프로그램이 사용자 입력을 읽고 처리하는 내용이면 키보드 입력 받기위해 프로세스를 키보드큐에 보내주고, 블락처리한다. 그동안 다른걸 수행하다가 키보드에 있던 애가 끝나면 키보드 컨트롤러가 cpu에게 인터럽트를 걸어서 알리고,cpu는 운영체제에게 프로세스상태를 ready로 바꾸도록해서 자신이 곧 실행할 수 있게 한다.

 

+공유데이터(면접질문!): IO 디바이스 서비스 기다리느라 block되는 경우 말고 소프트웨어 서비스 기다리느라 block되는 경우도 있다.

ex) 한 프로세스가 접근하는 도중에 cpu를 뺏겨서 다른 프로세스가 접근하면 일관성이 깨지므로 데이터를 막아둔 경우.

 

 

 

 

cpu가 프로세스 처리하는 과정

 

프로세스 실행) code가 stack에 쌓인다. 그 과정에서 data를 참조한다.

+JRE로 따지면 code,data영역이 코드와 static저장하는 method 영역 역할이고

stack은 jvm stack 역할이고 나와있진 않지만 heap은 그대로 heap역할을 하는듯하다.

 

인터럽트 등의 이유로 다른 프로세스로 문맥교환) 커널에게 요청(시스템콜)을 한다. pc의 주소는 프로세스주소공간이 아닌 커널 주소공간을 가리키게 된다.

 

운영체제-커널 동작) 프로세스 실행과정과 마찬가지이다. 다만, 커널은 자신의 data영역에 큐 자료구조를 만들어서 프로세스 상태를 변화시키며 순서를 바꾼다.

 

 

커널주소공간에서 프로세스들을 관리하는 모습

이때!!! 커널은 모든 프로세스가 접근할 수 있기 때문에 동시에 접근하는 경우를 막기 위해서

커널스택을 프로세스가 나눠가진다. 

 

+그리고 위의 큐들이 모두 커널의 주소공간-데이터 영역에 있기 때문에

운영체제가 프로세스 스케줄링이 가능한 것

 

 

커널이 한 프로세스에서 다른 프로세스에게 cpu 넘겨줄 때 context switch가 일어난다

프로세스가 cpu를 뺏겼다가 다시 cpu를 되찾을 때 처음부터 다시 하는게 아니라 하던 곳에서 하려면 그 문맥 (레지스터 상황,코드 위치)을 기억해둬야 한다. 즉, cpu를 뺏기기 전에 해당 프로세스의 정보들을 자신의 PCB에 저장을 해두고 나중에 되찾을 때 cpu하드웨어에 그때의 상황을 복구를 하는 작업을 운영체제가 해준다.

 

+반드시 인터럽트나 시스템콜이 발생한다고 context switch가 발생하는건 아니다. 예를 들어서, 프로세스 A->인터럽트 발생(프로세스A를 위한)->프로세스A 의 상황이라면 프로세스가 바뀌는게 아니기 때문에 context switch가 발생하지 않는다.  문맥교환프로세스가 바뀔때 정보를 저장하는게 목적임을 명심하자!

 

 

큐에 담기는 프로세스가 선입선출되는 다양한 기준

 

프로그램 시작전 / Job 스케줄러 / 장기)

메모리에 프로세스 올라가서 ready되기 직전에 올라와도 될지 안될지를 결정.

메모리에 몇 개 올려놓을건지를 기준으로 판단한다. degree of Muliprocess와 관련있다.

 

시작후 / Swaper / 중기)

메모리에 프로세스가 너무 많이 올라가있으면 몇 개를 쫓아냄.

 

시작후/ CPU스케줄러 / 단기)

굉장히 빨라야하고, 다음에 어떤 프로세스를 running할지 결정.

 

 

+중기 스케줄러가 확실히 성능을 개선시켜준다. 메모리를 뺏는거니까!

+Suspend상태는 사용자가 프로그램을 직접 중지시킨 경우고, 이땐 메모리에서 나와 디스크로 swap out된다.

 

 

동기식 입출력 비동기식 입출력

프로세스가 IO에 요청을 하고 완료될 때까지 자신의 일 못하는 것

scanf

프로세스가 IO에 요청해두면 자신의 일 할 수 있는 것

timeout

 

+프로세스를 너무 적게 올려놔도 안좋은 이유? 한 프로세스 IO로 블락된 동안 cpu놀고 있게 되니까.

 

 


스레드: 코드를 나눠서 처리할 수 있는 부분은 병렬적으로 따로 처리하는 것

 

Why?장점이 뭐길래?

 

->웹화면 객체 띄워지는걸 다 받아서 출력하는거보다 따로 처리해서 오는대로 보여주면 응답성 향상!

->0*10이 cpu하나면 하나하나 해야하지만 각각 나눠서 계산하고 나중에 합하는 방식도 가능

 

->일부 변수를 공유하기 때문에 프로세스 여러개 있는 것보다 단일 프로세스에 다중스레드 방식이 메모리 절약 됨.

 

어떻게 메모리를 절약하냐?

프로세스 하나->PCB 하나->프로세스상태값, 프로세스id, 메모리제한, pc, 레지스터 위치정보.

ㄴPCB는 운영체제(커널) 프로세스들을 관리하기 위해 자신만의 메모리를 가진 것이다.

 

'PC와 레지스터만'  스레드들이 각각 나눠저장하고 (코드를 쪼개서 따로 풀거니까 그 코드 위치도 따로 알고 있어야 됨)

나머지는 공유한다~~아래 그림을 참고하자

 

 

왼쪽과 오른쪽 그림을 잘 보자. 프로세스마다 주소공간을 가지고 있다.(그걸 읽으면서 동작하니까)

각 프로세스는 커널스택과 PCB 또한 나누어가진다.(정확히는 운영체제가 관리하기 위해서 프로세스별로 있는거임)

 

왜냐하면, 다중스레드 처리를 위해서 프로세스 PCB의 PC,레지스터를 스레드별로 구분해서 가져야 하기 때문이다.

실행할 코드를 push pop할 공간을 위해 프로세스 주소공간의 스택영역도 스레드별로 구분되어 있다.

결국 프로세스와 스레드가 공유하는 것은 data영역과 code영역이라는 것이다.(커널주소공간은 os를 위한 것이니 프로세스 관점으로 보자면)

 

 


공부한걸 바탕으로 아래 그림을 이해해보자.

 

 

 

 

공룡책 문제 빨리 풀어보자~~

 

 


멀티프로세스 멀티스레드
프로세스를 여러개 작동하는 것 한 프로세스를 이루는 스레드를 여러개 작동하는 것
cpu를 넘겨줄 때 문맥교환 일어남(비쌈) 문맥교환이 일어나지 않고 메모리 절약도 된다
ipc를 제외하곤 메모리 공유를 하지 않는다(전면마비 안옴) 힙을 공유하기 때문에 전면마비가 올 수 있다

 

 

개발하는 소프트웨어의 구조를 먼저 파악하자!

 

참고자료

https://gmlwjd9405.github.io/2018/10/27/webserver-vs-was.html

https://d2.naver.com/helloworld/819776


프론트엔드

 

반응형 웹?

반응: 자극에 의하여 어떤 현상이 일어남/그냥 이쁜게 아니다!

-> 같은 서비스를 디바이스 환경에 맞춰서 적절한 반응이 일어남

 

미디어 쿼리-서버랑 클라이언트는 아무것도 안함. 웹브라우저가 css코드를 읽고

해당 디바이스에 맞춰서 비율 바꿈.

 

이런 느낌~~? 하나의 파일에서 분기처리를 해서 구현한다!

 

good: 한 개의 파일에서 분기처리 가능

bad:레아아웃 복잡해지면 유지보수 어렵고 성능저하

 

적응형 웹은 분기처리 아니고 파일 여러개를 만들어서 서버,클라이언트에서 디바이스 체크 후

맞는 파일 불러와서 구현한다.(서비스 복잡할 때 좋음)

 

good:해상도별 최적의 성능 

bad:유지보수 어렵고 개발비용 많이 듬

 

전략-대응범위

계속 분기하면 끝도 없으니 대응 범위(디바이스,브라우저 범위) 정해야 함.

하위호환범위도 정해야 함(오래된 브라우저..)

->오래된 브라우저는 레이아웃 깨져도 되느냐?가 아니라 최소한의 UI를 보장하는 것!

->브라우저는 한 파일에서 동작하도록~

->안내메시지 사용

 

전략-큰화면->작은화면 구현?작은화면->큰화면 구현?

모바일이 위주면 후자가 될 수도 있는거!

 

 

전략-레이아웃 방식

플루이드 그리드-컬럼 넓이 값을 화면크기에 맞춰서 바꿈(상대단위 %em사용) //성능이슈 작다고 함

(컨텐츠 적어서 빈공간 많아질수 있으면 최대크기 제한가능)

 

컬럼 드랍-플루이드 그리드 했을때 가독성 떨어진다면, 폭 좁아질때 텍스트를 밑으로 내려버리는 방법

 

레이아웃 쉬프터-화면이 변할때마다 새로운 레이아웃을 제공함. 

 

섬네일 리스트-크기에 따라 보여주는 한줄에 보여주는 섬네일 갯수를 조정

 

+폰트&이미지리사이징(과도한 조정은 성능저하..) 

특히 아이콘 같은건 크기따라 리사이징 하기 어려워서 이미지를 애초에 여러개 만들어둔다.

(태블릿용,pc용..)

 

+플리킹-해상도 변경되면 고려, 터치기반이므로 pc화면에서 좌우버튼 노출..성능별로

 

//여기까지 다 반응형 웹이다!

 

[구축 순서] 폭-계층hierachy-상호작용-밀도

-컬럼 비율 좌측 본문 어느정도로 분배할건지 정하는 것. 해상도마다 그 비율 어떻게 다르게 할건지 정함

계층-화면 크기 줄이면 계층이 어떻게 변하는지 정하는 것. 

상호작용-햄버거라고도 부른다! 화면 작이지면 하나의 메뉴로 압축된다

밀도-화면이 작아지면 답답해질수 있기 때문에 컨텐츠를 제거하거나 크기 조절.

 

사진크기가 작아졌다! 

 

+Responsive Pattern(반응형 웹 템플릿 많이 있음)

+스타벅스 스타일 가이드 참고해서 만들면 짱!

 

 

더보기

_성능 이슈_

1.reflow repain 성능저하 (덧칠기법)

2.javascipt에서 이벤트 동작과 css의 미디어쿼리의 타이밍을 맞출 때 window.matchMedia 검색

3.폰트는 절대단위인 픽셀로 하면 관리 어렵다. em이나 rm(상대단위)로 선언하자!

-검증단계에서 디자이너랑 미리 상의 잘해야함~

4.네트워크 성능저하-공통 이미지와 해상도별로 나뉘어야 할 이미지를 구분 안하고 여러번 호출하면 발생.->css3.0사용하면 개선!

5.미디어쿼리 관리할때 css전처리기 사용하면 유지보수 쉽다~

 

 

더보기

_꿀팁_

크롬 에뮬레이터(개발자 도구)-해당 디바이스가 없어도 결과를 확인할 수 있음

프로젝트 준비기간과 프로토 타입 준비를 충분히 하고 하자. 

 

 


백엔드

뒤에서 데이터 처리하는 것. 

but! 쌓이는 데이터가 기하급수적으로 증가하고 있기 때문에 그것을 어떻게 견디는 구조를 만드는지가 주하다. 

 

사용하는 기술

운영체제-centOS,ubuntu,Window,Mac

언어-Java(고이느라 자바로 점점 바뀜..노하우가 자바로 쌓이니까)

개발환경-이클립스(공짜니까), 인텔리제이(다양한 환경 세팅을 도와줌,선호도 좋음,깃배포도 잘됨)

빌드툴(자바에서 바로 컴파일 하지는 않는다!왜??라이브러리 같은거 추가제거 할 때 신경 안쓰기 위해서)-아파치앤트,메이븐(디펜던시 관리 쉬움),그래들

버전관리툴(코드공유,협업을 위해서)-svn(공통저장소 하나만 있음)->git(각자 저장소가 있어서 코드리뷰 가능)

지속적인 통합(CI,로컬에서만 빌드하는게 아니라 개발자들이 각자 빌드하고 나중에 한꺼번에 빌드할 때 필요): 공통 레포지토리에 각자 코드 올림, CI서버에서 머지코드 가져와서 빌드해본다, 테스트코드 돌려본다, 정적분석 주기적으로 한다(널포인트 익셉션 발생할거 같다.등등..) 

/ex/ a가 커밋한걸로 ci서버에서 빌드했는데 실패하면 담당자들이랑 분석해보는거.

  ㄴ툴:젠킨스,허드슨

 

 

ㄴ위는 테스트 커버리지다! (어느 부분이 테스트 코드가 부족하다 등의 내용을 표시)

 

배포는 dev,staget,real 순으로 된다.

 

 

 

dev    crud 정도를 직접 입력할 수 있는 곳에서 돌려봄

stage 리얼서버와 동일한 cpu,메모리 환경 구성해서 돌려봄

기능 테스트까지 다 해보고

real 서버에 올림!

 

 

기능이 크고 규모가 크면 서버 100대씩 쓰기도 한다. 

하나하나 컴파일해서 톰캣 띄우고 하면 답이 없기 때문에

사내자동배포 시스템이 있음~~(원클릭)->dev stage real중에서 어디 배포할건지 조정해줌

 

"사용자 로컬에서 개발하고 배포서버에 요청하면 배포서버에서 공용레포지토리에 코드를 가져오고 각 배포환경에 보내준다"

 

웹서비스 구조(=서버 구성, 컴포넌트 구성)

 

웹서비스에서 컴포넌트란? ( https://imcreator.tistory.com/7 ,  https://imcreator.tistory.com/7)

 

 

하나의 서버가 10개의 클라이언트에게 정보를 제공할 때, 모듈은 2개를 가지지만 컴포넌트는 11개가 된다.

컴포넌트: 런타임 개체를 참조, 즉 객체라고 볼 수 있음. 독립적으로 배포될 수 있는 단위

모듈: 기능 중심으로 나뉘어질 수 있는 단위.

 

 

컴포넌트가 모여서 모듈을 이룰 수도 있고, 모듈이 이루어져서 컴포넌트를 이룰 수 있음.

 

 

웹서버에서 로드 밸런싱 일어나서

웹서버:WAS=' 1:1~다' 로 이루어짐

웹서버:정적페이지//얘만 브라우저(클라이언트)와 소통한다//네트워크프로그래밍 필요

was:동적파일 //비즈니스로직(서블릿의 실행흐름) 담겨있다//멀티쓰레딩 필요

캐시서버:게시판랭킹10위같은건 디비 자꾸 접근하면 부하 크니까 캐시서버 사용,.

배치서버(배치:일괄처리):사용자요청 받아서 10위같은거 받는거 말고,사내에서 뭐가 잘팔리는지 수익률,이런걸 데이터 정제해서 저장해두는곳. 

ㄴ스프링배치 굉장히 고도화 되어있어서 잘 사용한다.

+스프링: 위의 전과정을 할 수 있도록 지원해주는 라이브러리 집합소(툴로있음 STS Spring Tool Suite->여기서 비즈니스 로직 짤 수 있다):

비즈니스로직서버:네이버의 BLOC서버: 다양한 프로토콜 제공, 비즈니스 로직만 담고있는 컨테이너 플랫폼. 

 

네이버는 was로 톰캣 주로 쓴다. 

톰캣은 http만 반환하지만 BLOC은 다음과 같이 제공한다.

 

이런 복잡한걸 한눈에 볼 수 있는 모니터링 서버 개발하자는 의견 나왔음!

 

 

데이터 요청시 원하는 프로토콜로 요청하면 알아서 매핑해서 갖다줌.

 

 

 

 

http계속 맺고 끊기 싫어서 사내프로토콜을 tcp기반으로 따로 만들어서 쓰는중이다. 

각 서비스마다 선호하는 프로토콜 다름~

 

학생 개발자들이 오픈 api 쓰는거처럼

네이버는 여러 기업들에게 api를 제공하고 있다.

+api: 운영체제나 프로그래밍 언어가 제공하는 기능을 제어할 수 있게 만든 인터페이스.

+인터페이스: 서로 다른 장치에서 정보를 주고받을 수 있게 해주는 경계면

->즉, api라고하면 네이버장치와 삼성장치의 사이에서 네이버가 쌓아오고 정제한 정보를

삼성이 파라미터 던져주고 받게 끔 해주는 연결고리. 구현된 프로그램.

 

 

데이터베이스(오라클,몽고디비...다양하게 쓰고있다)

뭔말인지 모르겠네

샤딩이나 파티셔닝으로 좀 나눈다.

여러 사람이 같은 블로그 보는 경우?

->캐시서버,검색플랫폼,키밸류 씀

 

 

테스트 (단위테스트)+(기능테스트)+(부하테스트//ngrinder)

 

모니터링 (장애가 발생하면 담당자에게 문자감)

 

트러블슈팅->자동화 툴 없음!!없을 것!!<-도와주는건 있음(제니퍼 apm툴..덤프파일 보기 쉽게 되어있음) //네이버-Hello world에 삽질결론 많이 있으니 참고

 

특히..데이터가 계속 많아지기 때문에 원래는 되던게 안될수도 있다

대표적으로 이런 문제들 생김.. ->막기 위해서 IDC,Raid,로그수집시스템,서버증설바로할수있는 시스템,큐브리드(RDBMS),하둡 사용..

 

 

마지막!!!오픈소스!!!굉장히 선호한다!!!트렌디하니까~~

 

 

 

 

더보기

_성능이슈_

이때,새로운 서비스와 api 연동하고 싶은데 주는 파라미터가 기존보다 하나 더 추가되어있다면 기존에

연결되있던 연동서비스들의 파라미터는 어떻게 할지에 대한 이슈..

 

 

 

 


 

그 다음 요즘엔 웹어플리케이션 구조가 사용되고 있다. C/S구조에서 가장 큰 문제점은 매우 빠르게 변화하는 시대에 로직도 매우 빈번히 업데이트가 되어야 하는데, UI로직과 비즈니스 로직이 클라이언트에 있기 때문에 클라이언트에 있는 프로그램들을 서버측 데이터로직이 바뀔때마다 업데이트를 해줘야 한다. 이것이 매우 귀찮고 제대로 이루어지지도 않을 수 있기 때문에 아예 그냥 서버단에 UI로직 비즈니스 로직 데이터 처리 로직 등을 모두 놓겠다는 것이다.

 

정확히 말하면 웹어플리케이션 서버에 UI로직과 비즈니스 로직이 들어 있고, DMBS는 데이터 처리 로직을 담당하며, 웹 서버는 단순히 클라이언트의 요청을 받고, 웹 어플리케이션 서버를 멀티 쓰레딩 환경에서 실행시키는 중계자 역할만을 하게 된다.

또한 클라이언트는 웹서버가 내어준 응답을 단순히 화면에 뿌려주는 역할만 하는 어찌보면 메인프레임 시대의 단말기와 비슷한 기능을 하게 된다.

 

다시말해서, 서버단에 굉장히 여러개의 서버를 만들어 놓고 분산 처리를 하는 것이다. 이렇게 될 경우 매우 빠르게 변하는 요즘 같은 시대에 로직이 변하더라도 서버쪽만 바꿔주면 되기 때문에 적합하다고 할 수 있다.

 

웹 어플리케이션 서버에 배포된 자바로 구현된 웹 어플리케이션을 서블릿이라고 한다. 

 

 

출처: https://simsimjae.tistory.com/216 [104%]

 

 

1.PC와 서버 간의 데이터 전송은 웹브라우저와 웹서버간의 네트워크 프로그래밍으로 자동으로 이루어지기 때문에 개발자들은 더이상 직접적인 네트워크 프로그래밍을 신경쓰지 않아도 된다.

 

2.웹서버가 자동으로 웹어플리케이션서버를 멀티 쓰레딩 환경에서 작동하게 끔 해주기 때문에, 개발자가 이부분을 직접적으로 신경쓰지 않아도 된다.

 

개발자는 단순히 비즈니스 로직과 UI로직 데이터 처리 로직 딱 이 3부분만 구현하면 웹어플리케이션을 만들수 있게끔 편의가 제공 된것이다.

 

 

출처: https://simsimjae.tistory.com/216 [104%]


 

 

 

웹서버와 WAS의 차이?(https://victorydntmd.tistory.com/121)

웹서버: 정적인 컨텐츠를 제공(html,css,js) //아파치 //꺼내주기만 하는 창고라고 할 수 있다

WAS: 동적인 컨텐츠를 제공(DB조회, 로직 처리)  //톰캣  

하지만! 대부분의 was는 정적인 컨텐츠를 제공하기 때문에 웹 서버 없이 was만 존재 가능.

즈 , was는 웹서버를 포함하는 개념이다.

 

 

->그럴거면 웹서버를 굳이 앞에 두는 이유는??

1.정적인 문서만 처리하는 경우 서버의 부담을 줄일 수 있다. 

2.was의 환경설정 파일을 외부에 노출시키지 않도록 하기 위해서.(was접근포트와 웹서버 접근포트는 다르다)

 

로드밸런싱이란?

서버의 부하를 분산시키기 위해 가상 IP를 통해 여러 서버에 접속하도록 분배하는 기능. 

 

 

클라~브라우저+~웹서버~포트포워딩~WAS(톰캣:DB조회하는 로직있음)~커넥션 풀~DB

 

 

+서버를 증설한다?

예를 들어서, 로그인페이지(html)를 웹서버에서 주면 서블릿파일(톰캣 커넥션풀 코드 포함//원래 개발자가 우분투에서 이걸 직접 만듬)가 was에서 동작하는데, 이떄 html(정적)과 서블릿(동적)페이지를 연결할 때 스프링프레임워크(툴있음) 써서 연결하는거다. 트래픽이 몰려서 터질수 있으면 물리적 was를 몇 대 늘리고 로그인서블릿파일을 늘린 was에 배포후,

 포트포워딩으로 웹서버와 was를 로드밸런싱해준다. 그리고 db관점에서 자신에게 접근할 수 있는 커넥션을 풀에서 눌려주는 것.

 

*이클립스IDE는 jvm실행의 입출력을 GUI로 보여주는 것 뿐이다.

 

 

플랫폼: 개발환경, 웹서비스 만들면 jvm이 플랫폼임

플랫폼을 간략히 정의해 보면 소프트웨어를 실행할 수 있는 기반으로 볼 수 있다. 

라이브러리: 코드들의 집합(차로 치면 와이퍼,전조등..) 특정기능을 api로 호출해서 이용

프레임워크:라이브러리의 집합(소형차를 위한 자동차 프레임) 틀을 만들어두는 것

 

더보기

_사용하는 기술_

struts2,spring

mybatis(구ibatis) 데이터요청시 jdbc보다 좀 더 편하게,트랜잭션 관리 해주는 프레임워크,hibernate

네이버자체개발  사내 lucy웹 프레임워크<-요즘 좀 다른 목적으로 씀

 

 


 

 

급하게 오픽을 따야하는 분들을 위해서 제가 느낀 것을 알려드릴게요!

간략하게 저의 경우를 설명하면

 

2017년 8월->2주 동안 오픽 그룹과외로 '스크립트'를 외움 -> IL

2019년 9월->3일 동안 유튜브로 '표현'을 외움-> IM2

 

높은 등급은 아니지만 이전과 공부방식이 많이 달랐기 때문에 빠른 시간에 딸 수 있었던거

같아서 적어봅니다!!

 

제가 추천하는 방법은

유튜브 오픽노잼 시청 ->필러(you know,well..) 익히기, 감정표현 익히기(deep in anger..)->

랜덤하게 문제 대답해보고 녹음해서 듣기-> 끼워넣을 수 있는 표현 넣어서 다시 대답

의 무한반복!

 

IL 받았을 때는 만능 스크립트라고 해서, 비슷한 질문 나오면 써먹을 수 있는 스크립트를 만들어서

외워갔는데 솔직히 시간도 없는데 그 스크립트들 외우기도 힘들고 대화억양이 안들어가게 되서

티가 많이 나더라구요ㅠㅠ(직접 녹음해서 들어보기 전까진 몰랐어요!)

그리고 면접관들고, 좋아하는 공원 물어봤는데 when i was~로 시작하면서 과거 경험을 끌어오면

'아 얘는 공원에서 있었던 일 물어봐도 이걸로 대답했겠구나' 판단해서 바로 낮은 등급을 준다고 하네요. 

그러니 물어본거에만 집중!main point에 집중!하는게 중요합니다. 

보통 스크립트를 영화면 영화, 음악이면 음악 이렇게 주제별로 만들어두는데, main point에만 집중하면

1번 질문과 2번 질문이 같은 주제일떄 답변을 비슷하게 해야하는지 아닌지 난감한 경우도 없죠ㅎㅎ

 

그냥 아무것도 스크립트도 외우지 않고, 서베이 체크할 질문 아니더라도 새로운 문제에 부딪혀서 필러랑 감정표현

적용해보려고 노력하는게 도움이 많이 되는거 같아요. 

 

그리고 저는 개인 피드백 서비스(3만원)을 신청해서 세부진단서를 받을 수 있었어요.

하지만 음...추천하지는 않아요~~

이거 말고 3장의 종이가 더 있는데 다 IM2는 어떤 경우에 받게 되는지, 등급을 더 올리려면 어떻게 해야하는지에

대해서만 나와있어서 굳이 돈내고 신청할 필요는 없다고 생각이 들었어요. 

아래는 진짜 개인 피드백이긴 한데, 그래도 어느 부분을 보완해야하는지 체크해주고 

어떤 질문에서 잘 대답했고 어떤 질문에서 대답을 잘 못했는지 알 수 있어서

등급이 납득이 안가시는 분들께는 도움이 될 것 같아요!

 

 

                중간에 제가 broke down 했다고 하길래 뜻을 찾아봤는데 "고장났다"라는 뜻이더라구요ㅋㅋ너무해..

 

 

 

 

'Review > Thinking&ETC' 카테고리의 다른 글

부산 전자도서관 회원가입, 이용하는 법  (1) 2019.10.04

참고:

https://www.quora.com/What-are-command-line-arguments-1


 

Java를 처음 사용하기 시작했다면 

이런 에러를 종종 볼 수 있다.

 

 

 

원인은 여러가지가 있다.

1. main메소드를 선언하지 않았을 때

자바 프로그램이 실행되면 가장 먼저 하는 일이 main 메소드를 찾는 것이기 때문에,

이 에러가 발생하면 어떤 수행도 진행되지 않는다. 

 

2. public static void main(String[] args)에 오타가 있거나 임의로 수정했을 때.

위의 문장은 하나하나 의미가 있기 때문이다. 

 

public: 가장 먼저 호출해야하므로 접근에 제한이 있으면 안되므로 무조건 public이어야만 가능하다.

생략하는 것도 안된다. 생략하면 접근제어자가 default(같은 패키지에서만 접근 가능)가 적용되기 때문이다. 

 

static: 아까 말했듯이, 어떤 코드보다 가장 먼저 실행되어야 하므로 객체를 만드는 코드 이전에 작동하기 위해선

static을 붙여야한다. 그렇게하면 인스턴스 생성이 되지 않아도 접근이 가능하기 때문이다. static을 붙이면 인스턴스에 속해 있지 않고 클래스 범위로 소속되기 위해 JVM메모리의 method 영역에 정보가 올라간다. 

 

void: main메소드가 가장 먼저 시작했기 때문에 main이 반환한 값을 처리할 메소드는 없기 때문에 반환형은 void이어야만 한다.

 

main: main이란 키워드로 메소드를 찾기 때문에 그대로 써줘야한다. 

 

 

String args[]: 나는 처음에 String args[]를 써야하는 이유에 대해 궁금했었다.일단 위치는 매개변수 자리이다. 즉, 이 클래스 파일을 실행할 떄 문자열 배열 정보를 함께 준다는 것이다. 그 말은 즉, 프로그램을 모듈화 할 수 있다는 뜻이다. 예를 들어서, 하나의 메인 메소드가 실행될 때 args에 담긴 문자열이 "삭제"라면 삭제에 관한 함수를 실행시키도록 하는 것이다. 추가로 주는 정보에 따라서 여러 동작을 수행할 수 있도록 모듈화 하는 것이다. 일반적인 메소드에서의 매개변수를 떠올리면 되지만, main의 매개변수는 이런 모듈화 성격이 더 짙다. 

 

커맨드라인(이클립스 IDE가 아닌 cmd창에서 텍스트 기반으로 프로그램을 만드는 경우)에서는 바로 명령어를 args에 넣을 수 있다. 하지만 이클립스 IDE에서는 GUI 기반으로 Run Configurations..->Arguments->Program arguments에서 원하는 명령어를 넣을 수 있다. 

 

처음 JAVA로 프로젝트를 할 때, 오픈소스를 사용하는데 입력 받는 함수가 없을 때 인풋(입력값)을 어디에 넣어야 하는지 헤맨 적이 있었다. 그때 찾은 간단히 해결하는 방법이 이것이다. 

 

3. 수정된 파일을 저장하지 않고 컴파일 했을 때

초보일 때 이 에러의 가장 많은 원인이라고 생각한다.  나도 아직 초보이긴 하지만.. 프로젝트를 할 때 종종 main을 찾을 수 없다고도 나오고, 이전에 작성한 코드의 결과값이 출력되는 경우가 더러 있었다.

 

당시에 검색을 했을 때는 이클립스에서의 에러보단 콘솔에서의 코드 오탈자로 설명하는 글이 많았다. 콘솔에서 main을 찾을 수 없는 문제는 대부분 .java 파일을 컴파일 할 때 확장자까지 모두 써줬던 것처럼 .class파일 또한 끝까지 다 쓴 경우 생기는 에러였다. (클래스파일은 실행만 바로 하면 되는 .exe 프로그램이기 때문에 이름만 써주면 된다!)

 

 

하지만 이클립스에서는 코드를 저장하지 않아서 생기는 문제이다. 정확히는, 컴파일을 하지 않고 run을 해서 생기는 문제이다. 이클립스에서 컴파일은 단축키가 ctrl+s이다. main을 작성하지 않고 저장한 상태에서 main을 작성하고 곧바로 run을 하면 에러가 발생한다.

 

 

 

여기서 ctrl+s로 저장 및 컴파일을 하고나서 실행을 하면 잘 실행이 된다. 

 

 

만약 main을 찾을 수 없는 상황이더라도, 같은 프로젝트에서 main이 존재하는 클래스가 있다면 에러 메시지를 출력하지 않고 그 클래스의 main 메소드를 실행하게 된다. 

그러니 실행 전엔 항상 저장을 하는 습관을 들이자!

 

참고자료

인프런 자바 프로그래밍 입문 강좌 ,

오라클 Java Language Specification ,

https://docs.oracle.com/javase/specs/jvms/se12/html/jvms-2.html

그 외 블로그..

https://minwan1.github.io/2018/06/06/2018-06-06-Java,JVM/

https://sehun-kim.github.io/sehun/JVM/


 

객체지향프로그래밍(Object Oriented Programing)

객체끼리 상호작용하는 프로그래밍 방법. JAVA, C++...

 

객체(Object)

포괄적 의미: 속성을 가진 모든 것. 사람(키,혈액형,이름 등의 속성이 있음),과자(당도,모양 등의 속성이 있음)

프로그램 관점: 객체를 데이터화 해서 실제 메모리에 올린 것

 

클래스(Class)

객체를 어떻게 데이터화 시킬건지 정의하는 설계도

속성 정의(어떤 값을 가짐), 메소드 정의(어떤 동작을 수행)

 

->즉, 객체지향프로그래밍은 

클래스에서 객체를 뽑아내서 그 객체들을 이용해서 어떤 동작을 수행하는 프로그램을 만드는 것이다.

Car라는 클래스에서 포르쉐,아우디,벤츠라는 객체가 만들어지면 3대의 자동차가 레이싱을 하는 프로그램을 만들 수 있다.

 

->장

클래스와 패키지로 구분되기 때문에 잘 만들어진 설계도를 가져와서

내가 원하는 기능을 추가해서 쓸 수 있다. 혹은 설계도를 나눠서 만들 수 있어서 협업이 편하다.

더보기

내 경험:

->단

설계도를 재사용을 하기 때문에 설계시 많은 고민이 필요하다.

객체를 많이 만들면 처리하는 속도가 느려진다.

더보기

내 경험:


JAVA 동작 과정

 

 

"개발자"가 자바 코드를 작성하고 코드 파일을 저장한다. 이때, 자바 코드 파일의 확장자는 .java이다.

"자바 컴파일러"는 .java파일을 .class파일로 만든다. (이 시기를 컴파일 타임이라고 한다)

더보기

클래스 파일로 만든다는 것은 바이트 코드로 만든다는 것이다.

바이트 코드란? JVM이 읽을 수 있는 코드를 뜻한다. cpu가 0110101과 같은 이진코드를 읽을 수 있듯이 cpu역할을 하는 jvm은 아래와 같이 생긴 바이트 코드를 읽을 수 있다.

jvm이 바이트코드를 읽는 것은 자바의 독립성과 관련이 있다. 이 페이지 하단에서 설명할 것!

"jvm"은 .class파일을 읽어서 코드가 나타내는 동작을 수행한다. (이 시기를 런타임이라고 한다)

 

이때, JVM이 동작하기 위해선 JRE가 있어야하고 JRE는 JDK에 포함되어 있다.

이게 무슨 말이냐?? 꽤 중요한 부분이니 집중하자!!

 

 

JVM(Java Virtual Machine):

자바가상머신, cpu칩과 같은 역할을 하는 소프트웨어 칩이다. cpu칩이 기계어 코드를 읽는다면 JVM은 바이트 코드를 읽는다. 클래스 파일을 실행시킬 때 동작하기 때문에 런타임의 엔진에 속한다.

JAVA와 OS사이에서 바이트 코드를 기계어로 변환시켜준다. 

이 말은 즉, JAVA는 이식성이 좋다는 것을 의미한다. 

어떤 OS에서 한번 클래스파일을 만들고 나면, 다른 OS에서 별다른 수정없이

클래스파일을 실행시켰을 때 동일한 결과가 나오기 때문이다.

 그래서 JAVA는 OS에 독립적이고, JVM은 종속적이라고 할 수 있다.

또 JVM에 포함되는 GC(가비지 컬렉터)는 힙의 메모리를 알아서 관리해준다.

C언어 같은 경우 메모리를 할당한 뒤 사용이 끝나면 직접 해제를 해줘야 하는데

JAVA는 GC가 존재하기 때문에 메모리를 효율적으로 관리할 수 있다.

그래서 JAVA와 C의 가장 큰 차이를 꼽는다면, JVM의 유무라고 할 수 있다.

 

JRE(Java Runtime Environment):

JVM과 JVM이 동작할때 필요로 하는 파일들을 포함한다. 이름 그대로 컴파일타임이 아닌

런타임의 환경이므로 소스파일이 아닌 클래스파일일때 작동한다.

 

JDK(Java Development Kit):

JRE와 javac.exe 등을 포함한다.

자바로 프로그램을 개발하는데 필요한 것을 모아둔 키트이다. 

소스파일에서 클래스파일이 되기까지의 과정을 가능하게 해주므로

컴파일타임을 위해서 javac(자바컴파일러)를 포함하는 것에 주의하자.

 

 

모두 Oracle사이트에서 다운로드 받을 수 있다.

+여담이지만, 자바를 제임스 고슬링이 썬이란 기업에서 발명했고 썬은 오라클에 인수되어

현재 오라클이 자바 업데이트를 관리하고 있다. 일반적으로 java로 개발을 하려면 jdk를 설치하면 된다.

현재 jdk최신 버전은 12이며 8이 가장 안정화되어 있다.

목적에 따라 SE(개인용),EE(기업용),ME(임베디드용)로 나뉘어져 있는데 처음 시작한다면 SE를 설치하면 된다.

 

+필요에 따라 jdk를 설치하지 않을수도 있다.

가령, 이클립스 툴을 통해 Java 어플리케이션을 개발한다면 jdk가 필요하다.

하지만, Tomcat 웹서버를 구동하기 위해서는 jre만 필요하다.

Tomcat은 사용자 입장에서 Class 파일만 구동시킬 수 있으면 그만이기 때문이다.


cmd에서 메모장으로 자바 코딩

*파란색 글씨는 입력해야하는 명령어입니다*

*jvm이 설치되어 있고 환경변수까지 설정되어 있어야 합니다*

 

 

0. cmd창에서 파일을 저장할 위치로 이동한다. (cd+ 해당 위치)

더보기

정말정말 초보를 위해서.. 위치를 알아내는 법! 동그라미 친 부분을 클릭하면 경로 복사가 가능하다

C:\~~에 뭐가 더 붙었을 것이다..현재 위치가 바뀌었다는 말! 이젠 메모장 실행 (notepad)

 

1. 메모장에서 프로그램이 수행할 동작을 JAVA 문법을 사용한 코드로 작성하고

파일 이름은 Test.java로 저장한다 (클래스이름+확장자 .java)

 

2. 지금 위치에 코드 파일이 잘 저장되었는지 확인 (dir)

 

3.자바 컴파일러를 불러와서 컴파일 해준다. (javac + java파일이름.java)

4. 2번과 마찬가지로 확인해보면 기존의 java파일과 새로 생성된 class파일이 있다.

5.class파일을 jvm으로 실행시킨다. (java + class파일이름)

!!주의할 점!!

4번에선 파라미터의 이름이 Test.java이지만 5번에선 Test이다.

5번에서 확장자를 적으면 에러가 발생한다.

이유는 나의 추측이지만,class파일로 만들면서 이미 문법의 유효성이나 확장자로 파일 내용의 유효성 검사를 마쳤고,

실행만 하면 되기 때문 아닐까싶다.

 

이 모든 과정을 우리는 시꺼먼 cmd 대신 이클립스라는 보편적이고 깔끔한 IDE로 진행할 수 있다.

IDE에서 코드를 작성하면 javac와 같은 명령어는 입력하지 않아도 된다.

ctrl+s가 javac역할을 하고

run버튼이 java역할을 하기 때문이다.

 


JVM 동작 과정

 

위 그림의 큰 사각형에 포함되는 부분을 JVM 아키텍처라고 부른다.

JVM은 Class Loader,Runtime Data Areas,Execution으로 구성된다.

 

1. 컴파일러가 소스코드를 컴파일해서 클래스파일로 만들고 난 뒤, 클래스파일을 실행한다.

-JVM 동작 시작-

2. 클래스로더가 바이트코드를 Runtime Data Area에 로딩한다.

3.4.3.4..바이트코드는 Execution 엔진에 의해 해석된 후 해석된 코드는 Runtime Data Areas의 각 영역의 목적에 맞게 배치되며 실질적인 수행이 이루어진다. 

 

 

 

Runtime Data Areas

 

프로그램이 실행될 때 JVM이 운영체제로부터 할당받는 메모리 영역이다. 

생성 시기에 따라 분류하면 다음과 같다.

 

1.쓰레드가 시작될 때마다 생성(쓰레드마다 할당받는 메모리)

PC Resiter

여러 쓰레드들은 자신이 현재 실행중인 메소드의 주소를 저장하고 있다.

 

JVM Stack(

스택프레임(메소드 단위)으로 push(메소드 시작) pop(메소드 종료) 연산을 수행한다. 

각 '스택프레임'은 'Local Variable Array 지역변수배열'와 'Operand Stack 피연산자 스택'의 참조값을 저장한다. 이 값들은 컴파일시에 크기가 결정 되었으므로 JVM스택의 크기는 쓰레드가 실행되는 초기에 고정된다. 그러므로 이 크기를 넘는 경우 Stack Overflow가 발생할 수 있다. 

이 과정에서 JVM스택은 힙과 함께 동작한다.

 

Local Variable Array:

0은 this 주소

1부터 매개변수

그 뒤는 지역변수

 

Operand Stack:

메소드의 실제 작업공간. Local Variable Array와 데이터를 교환한다. 다른 메서드 호출 결과를 push pop한다.

 

Native Method Stack

C/C++ 코드를 수행하기 위한 스택이다.

 

2.모든 쓰레드가 공유

Method Area,Runtime Constatnt Pool

JVM이 시작될 때 각 클래스의 구조를 텍스트(바이트코드)로 저장하는 영역이다.(컴파일때 크기가 정해져서 크기 고정)

Rutime Constant Pool,필드 코드, 메소드 코드를 포함한다.

특히 Runtime Constant Pool에선 모든 메서드,인터페이스의 실제 주소를 가지고 있다. 

이 영역에 있는 클래스 정보로 Heap 영역에 객체를 생성한다.  

 

 

Heap

런타임에 할당되는 영역이며, 모든 클래스와 배열의 객체가 저장되는 곳이다. GC(따로 포스팅) 의 대상이며 동기화 처리가 일어나는 영역이다. GC에 의해 자동적으로 버릴 메모리를 관리하며 정해진 힙의 크기를 넘어가면 OutOfMemoryError가 발생한다. 

 

 

 

 

  • JRE가 자바프로그램을 실행시킬 때 맨처음으로 main()를 찾는다.
  • 있으면 Class Loader가 목적파일(.class)을 실행시킴
  • static 영역에 java.lang 패키지, import한 패키지들을 위치시킨다. 프로그램에 있는 모든 클래스와 필드 ,메소드가 올라감
  • stack영역에 main()의 Stack Frame이 위치하고 변수 영역에 인자를 위치시킨다.
    • 지역변수의 경우 선언이 아닌 초기화 될 때 위치된다.
    • 클래스 선언 {}을 제외하고 메소드의 {}, if의 {}이 생길때 마다 Stack Frame이 생긴다.
  • 메소드를 실행
  • ”}”괄호를 만나게 되면 Stack Frame이 사라진다.
  • JRE는 JVM을 종료시키고 위치했던 메모리들이 모두 없어진다.

 

 

 

 

 

Execution Engine

Runtime Data Areas에 배치된 바이트코드를 명령어 단위로 읽어서 실행한다.

그 방식은 Interpreter, JIT 두 가지가 있다.(다른 포스팅) 

 

 

 

 

https://www.acmicpc.net/problem/17349

 

17349번: 1루수가 누구야

(1 2)가 거짓말이라면, 선수 2가 1루수라는 주장과 1루수가 아니라는 주장이 동시에 존재하여 모순이다. (0 4)가 거짓말인 경우도, 마찬가지의 이유로 모순이다. (0 2)가 거짓말인 경우, 선수 2를 유일한 1루수로 특정할 수 있다. 다른 경우는 없다.

www.acmicpc.net

[주의할 조건]

정확히 한 명이 거짓말을 하고 있다.

정확히 한 명이 1루수로 정해지는 경우만 등번호를 출력한다.

 

[구하는 값]

1루수로 정해진 선수의 등번호

 

[생각의 흐름]

이런 논리적 흐름으로 결론을 도출하는 문제는 조건 하나가 주어지면

그 조건으로 결정되는 것들은 모두 체크를 해주어야 한다. (구슬찾기 문제가 생각난다..)

이 문제에선 여러 주장들을 처음에 주는데, 각각의 선수들이 거짓말을 하는 모든 경우를 계산해봐야 한다. 

 

그래서 나는 def()를 통해서 포문으로 

1번 선수가 거짓말을 했을 때-> 누가 1루수로 정해지는지 를 lie(1)로 반환.

2번 선수가 거짓말을 했을 때-> 누가 1루수로 정해지는지 를 lie(2)로 반환.

...

이 순서로 검사를 했다. 누가 거짓말을 하든 한 사람을 특정해야 하므로 

lie() 값이 바뀌면 def()는 -1로, 바뀌지 않고 무사히 종료되면 def()는 그 값으로 종료된다.

 

 

lie(i)는 다음과 같이 동작한다.

<1> i의 주장만 반대로 선수의 1루수 여부를 배열에 체크한다.(i가 누군가가 참이라고 주장했으면

나머지는 모두 1루수가 아니라는 체크도 해줘야 한다.)

<2>나머지 선수는 모두 참인 것을 가정하여 선수들의 1루수 여부를 배열에 체크한다.(마찬가지로, 참을 주장하는 경우 나머지가 모두 1루수가 아니라는 것을 체크해줘야 한다)

...

이 과정을 거치면서 lie(i)는 중간에 모순을 발견하거나, 모순 없이 완료가 된다.

 

//모순을 발견한 경우

<2>를 실행하는 도중에 x로 체크되어 있던 곳에 o를 체크해야하는 상황이 오거나 그 반대일 경우, 모순이다.

이런 경우에는 lie(i)를 -1로 나오고 쓸모없는 가정이라 무시하고 다음 lie를 계산한다

 

//모순이 없는 경우

모순없이 실행이 완료되었으면 누가 1루수로 모아졌는지 확인을 해야한다.  이때 선수들의 상태값은 참(o),거짓(x),미정(?)으로 존재한다. 다음이 그 예시이다.

 

oxxxxxx

oxx?xx?

?xxxxxx

 

o를 기준으로 생각하자.

 

o가 2개 이상 나오는 경우? 는 불가능하다. <2>를 실행하며 모순으로서 나갔을 것이다.

o가 1개인 경우? 지금은 이미 누가 거짓말을 하고 누가 진실을 얘기했는지 정해진 상태이기 때문에, 더이상 무언가가 거짓일 가능성은 생각하지 않으므로,그 o를 믿어도 된다. 1루수가 그 o로 정해진다.

o가 0개인 경우? 예를 들어서 모두의 주장이 '2번선수는 1루수가 아니다'일 경우이다. 정보가 하나밖에 없으므로 누군가를 o라고 할 수도 없지만 x라고 할 수도 없다. 즉 1,3,4...가 1루수일 가능성이 있는 것이다. 모순이 없었기 때문이 이 가능성은 유효하다. 이런 경우, 특정지을 수 있는 인물이 2명 이상이 되므로 이 경우 lie(i)를 넘어서 def()를 -1로 나와야한다. 

 

!!!누가 거짓말을 하든 1루수가 하나로 특정되어야 한다. 나는 이 부분을 착각해서 몇 번 틀렸었다!!!

 

 

다시 def()로 돌아와서,

 

모든 i=1,2,3,...에 대해서 lie(i)가 한 사람을 특정하면 그 값으로 답을 반환하고

 

어떤 lie(i)가 모순없이 두 사람 이상을 특정하면 -1로 값을 반환하고

 

어떤 lie(i)가 모순이면 그 값은 버려진다.

 

 

->코드가 좀 더럽다ㅠ

#define _CRT_SECURE_NO_WARNINGS
#include<iostream> 
#include<cstring>
#include<vector>
using namespace std;
int rusu[10];
int talk[10][2];
int lie(int num);
int def();
int main() {

	freopen("Text.txt", "r", stdin);
	for (int i = 1; i <= 9; i++) {
		cin >> talk[i][0] >> talk[i][1];
	}
	
	cout << def();
	return 0;
}
int def() {
	int tmp;
	int ans = -1;
	for (int i = 1; i <= 9; i++) {
		memset(rusu, 0, sizeof(rusu));
		tmp = lie(i);

		if (tmp == -2)
			return -1;
		if (tmp >0 && ans == -1) {
			ans = tmp;
		}
		else if (tmp > 0 && ans>0) {
			if (ans == tmp)
				continue;
			else
				return -1;
		}
	}
	return ans;
}
int lie(int num) {

	if (talk[num][0] == 1)
		rusu[talk[num][1]] = -1;
	else {
		rusu[talk[num][1]] = 1;
		for (int i = 1; i <= 9; i++) {   //하나 결정되면 완벽히 차단...결정될 수 있는거 지금 다 결정한다는  뜻
			if(i!= talk[num][1])
				rusu[i] = -1;
		}
	}
	for (int i = 1; i <= 9; i++) {
		if (i == num)
			continue;
		if (talk[i][0] == 1) {
			if (rusu[talk[i][1]] == -1)
				return -1;
			rusu[talk[i][1]] = 1;
			for (int j = 1; j <= 9; j++) {
				if (j != talk[i][1]) {
					if (rusu[j] == 1)
						return -1;
					rusu[j] = -1;
				}
			}
		}
		else {
			if (rusu[talk[i][1]] == 1)
				return -1;
			rusu[talk[i][1]] = -1;
		}
	}
	vector<int> one;
	vector<int> zero;
	for (int i = 1; i <= 9; i++) {
		if (rusu[i] == 1)
			one.push_back(i);
		else if (rusu[i] == 0)
			zero.push_back(i);
	}

	if (one.size() == 1)
		return one[0];
	if(one.size()==0&&zero.size()==1)
		return zero[0];
	return -2;
}

 

 

 

 

in:
0 1
1 1
0 2
0 3
0 4
0 5
0 6
0 7
0 7
out:-1

 

이건 내가 틀렸을 때 유용했던 반례이다. 

 

 

문제가 참 생각할게 많고 귀찮다~~혹시나 답답해서 풀이를 찾고 있는 분께 도움이 되었기를..

 

'PS' 카테고리의 다른 글

프로그래머스 배달  (0) 2019.10.25
백준 17090 미로 탈출하기  (0) 2019.08.14
백준 13251 조약돌 꺼내기  (0) 2019.08.13

https://www.acmicpc.net/problem/17090

 

17090번: 미로 탈출하기

크기가 N×M인 미로가 있고, 미로는 크기가 1×1인 칸으로 나누어져 있다. 미로의 각 칸에는 문자가 하나 적혀있는데, 적혀있는 문자에 따라서 다른 칸으로 이동할 수 있다. 어떤 칸(r, c)에 적힌 문자가 U인 경우에는 (r-1, c)로 이동해야 한다. R인 경우에는 (r, c+1)로 이동해야 한다. D인 경우에는 (r+1, c)로 이동해야 한다. L인 경우에는 (r, c-1)로 이동해야 한다. 미로에서 탈출 가능한 칸의 수를 계산해보자. 탈출 가능한

www.acmicpc.net

 

[주의할 조건]

(r , c)에 적힌 문자에 따라서, 가야하는 방향이 정해져 있다.

 

[구하는 값]

그 칸에서 이동을 시작했을 때, 미로의 경계 밖으로 이동하게 되는 칸이 총 몇 칸인지 구한다.

 

[생각의 흐름]

 

1. 모든 '그 칸'에 대해서 시뮬레이션을 해보고, 결과에 따라서 정답의 카운트를 올려준다. 

현재 위치한 칸에 어떤 문자가 적혀있는지에 따라서 갈 방향이 정해져있기 때문에

내가 따로 특별한 조건이나 로직을 설정할 필요가 없다. 

즉, 요소에 움직일 수 있는 동력(?)을 불어넣고 알아서 갈 수 있는 방향으로 움직이다가

경계 밖으로 나온 것만 카운트 해준다..'칸마다 경로가 유일하므로' 큐에 넣었다가 큐에서 요소가 빠질

때 카운트가 곧 정답이다.

 

~~라고 생각하면 함정이 보인다. 요소가 큐에서 언제 빠지는가? 항상 빠질 수 있는가? 만약 맵이

RD

UL

이렇게 생겼을 때 R에서 출발하면 빙글빙글 무한루프에 빠진다. 그렇다고 visit을 체크해버리면

RDU
URU

DRL

이런 맵에서 (1,3)인 U가 먼저 큐에 들어갔다가 나와서 visit 체크를 해뒀는데 

(2,3)인 U에서 올라가지 못하게 된다.  

 

2.생각을 다르게 해봐야한다!

경로가 하나 있으면 그 안의 요소들은 한 경로에 포함이 된다는 것에 주목하자.(이 문제를 해결해야한다)

그렇다면 그 경로를 대표할만한 요소는??경로의 끝, 경계 부근에 있는 요소일 것이다.

맵의 어떤 요소이든 가장자리를 거쳐서 경계 밖으로 나갈 것이다.

그 말은 즉, 가장자리에서 출발하되 역으로 이동을 하면 해당 요소들을 모두 만날 수 있다는 뜻이다.

가장자리에 있는 요소들 중에서 바로 경계를 벗어날 수 있는 요소들만

큐에 넣고, 역방향으로 이동하며 만나는 요소들을 모두 카운트 해준다. 

 

물론 역방향으로 이동할 때엔 네방향을 모두 봐야한다.

이전 칸에서 지금의 칸으로 올 때 왼쪽으로 꺾어서 왔는지 내려왔는지 올라왔는지 모르기 때문이다. 

그렇게해서 만난 요소는 나가는 것이 가능한 칸이라고 결정이 난 것이고, 그 요소를 다시 확인할 필요는 없다.

그래서 visit으로 체크를 해준다. 

마치 동굴의 입구를 찾아서, 길을 뚫어내며 갇혀있던 사람들을 구하는 것과 같다. 이미 만난 사람은

내가 뚫어온 길로 가면 되니 해결된 사람이고, 나는어디 처박혀 있는 친구들을 더 찾으러 깊이 들어가는 셈이다.

(예전에 탈옥 문제가 생각난다..ㅎㅎ)

 

+사실, 무한루프에 빠지는 경우가 생각나지 않더라도 500x500의 맵에서 최악의 경우를 가정했을때

(1,1)에서 지그재그로 모든 요소를 거쳐서 (500,500)으로 빠져나가는 경우를 생각하면

그 사이의 요소들이 같은 경로를 통해 (500,500)으로 다시 빠져나가하므로

시간복잡도가 n^2이 되는 것을 알 수 있다. (요소가 총 25만개이므로 시간초과가 날 시간복잡도이다.)

그냥 대충 큐에 때려넣어버리면 안되는 문제!!

 

 

 

 

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<queue>
using namespace std;
struct pos {
	int x, y;
};
int offset[4][2] = { { 0,1 },{ 1,0 },{ 0,-1 },{ -1,0 } };  //R D L U
int map[501][501];
int visit[501][501];
int main() {
	//freopen("Text.txt", "r", stdin);
	int n, m, cnt = 0;
	
	queue<pos> q;
	cin >> n >> m;
	for (int i = 0; i < n; i++)
		for (int j = 0; j < m; j++) {
			char ch;
			cin >> ch;
			if (ch == 'R')   //입력받을때 문자 대신 숫자로 입력
				map[i][j] = 0;
			else if (ch == 'D')
				map[i][j] = 1;
			else if (ch == 'L')
				map[i][j] = 2;
			else
				map[i][j] = 3;

			int x = i + offset[map[i][j]][0];  //바로 다음 칸이 경계를 넘는지 확인
			int y = j + offset[map[i][j]][1];
			if (x < 0 || y < 0 || x >= n || y >= m) {
				pos p;
				p.x = i;
				p.y = j;
				visit[i][j] = 1;
				q.push(p);  //뚫린 가장자리라면 큐에 넣음
				cnt++;
			}
		}

	while (!q.empty()) {   
		pos cur = q.front();
		q.pop();
		for (int d = 0; d < 4; d++) {
			int x = cur.x + offset[d][0];
			int y = cur.y + offset[d][1];
			if (x < 0 || y < 0 || x >= n || y >= m || visit[x][y] == 1)
				continue;
			if (map[x][y] == (d + 2) % 4) {  //역방향으로 나간다
				visit[x][y] = 1;
				pos pn;
				pn.x = x;
				pn.y = y;
				q.push(pn);
				cnt++;
			}
		}
	}

	cout << cnt;
	return 0;
}

 

 

 

 

 

 

'PS' 카테고리의 다른 글

프로그래머스 배달  (0) 2019.10.25
백준 17349 1루수가 누구야  (0) 2019.08.14
백준 13251 조약돌 꺼내기  (0) 2019.08.13

https://www.acmicpc.net/problem/13251

 

13251번: 조약돌 꺼내기

첫째 줄에 뽑은 조약돌이 모두 같은 색일 확률을 출력한다. 정답과의 절대/상대 오차는 10-9까지 허용한다.

www.acmicpc.net

 

[주의할 조건]

색상의 종류: M개 (1~50)

조약돌 총 갯수: N개 (1~50*50)....각 색상의 조약돌 개수는 1<=x<=50 이므로 

뽑는 돌 갯수: K개 (1~50*50)

출력값의 오차는 10^-9까지 허용....소수점 이하 10자리까지 구하면 오차 내의 범위로 구해진다.

깔끔하게 %.15lf로 구하면 됨!

 

[구하는 값]

뽑은 조약돌이 모두 같은 색일 확률

 

[생각의 흐름]

1. 처음에는  분자: 전체 조약돌에서 뽑는 경우의 수 , 분모: a컬러로 모두 뽑는 수+ b컬러로 뽑는 수....

로 둬서 분모/분자 = (aCk + bCk + cCk ...) /(nCk) 를 계산하려고 했다. <-a: 색상이 a인 돌의 갯수

 

하지만!!그렇게하면 DP로 풀어도 계속 오답이 뜬다..

조합

이 문제를 풀어보면 알겠지만, 

100C50 = 100891344545564193334812497256

정도되는 아주아주 큰 수이면 long long의 범위조차 벗어나기 때문에 

string을 사용해서 big integer를 구현해야한다. 한마디로 십진수 덧셈을 구현해야 한다는 말...ㅎㅎ

조약돌 구하기 문제에선 2500C50도 구할 수 있어야하기 때문에

처음 생각한 방법으로 풀면 오답이 나올 수 밖에 없다. 그렇다고 이 문제에서 big integer를 구현하기엔...

너무 귀찮다ㅠㅠ

 

2.그래서 아예 확률을 구하는 식 자체를 다르게 잡아야한다. (이 문제 분류가 '큰 수'가 아닌 '확률'인 이유다.)

경우의 수를 다 구해서 확률을 계산하지 말고, 큰 수가 나오기 전에 미리미리 확률로 만들어두면 된다.

즉,  "각 컬러에 대해 뽑는 모든 경우의 수/ 모든 경우의 수"를 계산하는 것이 아니라

"a컬러에 대해 뽑는 확률"+"b컬러에 대해 뽑는 확률" + ....을 구해야하는 것이다.

 

 

n=200 , m= 50, k=3 에서 a컬러의 돌갯수가 40이라고 가장하자. 

 

a컬러로만 모든 돌을 뽑는 확률

= (40/200)*(39/199)*(38/198)= (40*39*38)/ (200*199*198).

 

처음부터 조합식으로 바로 풀려고 하면 쉽게 오답이 뜰 수 있는 문제이다. 

이런 식으로 각 컬러별로 확률을 구해서 모두 더해준 뒤, 소수점 이하 15자리까지 출력을 하면 통과할 수 있다. 

 

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
int color[50];
double p[50];
int main() {  
	int n = 0, m, k;
	scanf("%d", &m);
	for (int i = 0; i < m; i++) {
		scanf("%d",&color[i]);
		n += color[i];
	}
	scanf("%d",&k);

	for (int i = 0; i < m; i++) {
		if (color[i] < k)  //!!!!이 경우는 넘어가야함. 2개중에서 4개 못고르니..
			continue;
		double tmp = 1.0;
		for (int j = 0; j < k; j++) {  //k를 빼서 0이되는 경우 있을수도 있으니까..
			tmp *= (double)(color[i] - j) / (n - j);
		}
		p[i] = tmp;
	}
	double ans = 0;
	for (int i = 0; i <m; i++) {
		ans += p[i];
	}

	printf("%.15lf",ans);  
	return 0;
}

'PS' 카테고리의 다른 글

프로그래머스 배달  (0) 2019.10.25
백준 17349 1루수가 누구야  (0) 2019.08.14
백준 17090 미로 탈출하기  (0) 2019.08.14

+ Recent posts