http://www.tiptech.net/lcture/asp/basic/whatasp.html

http://minsookim.tistory.com/entry/ASP-ASP-%EB%9E%80

http://blog.naver.com/mobilism/70151511306

http://blog.daum.net/ssc1978/13852608


ASP란?

Active Server Page의 약자로, 동적으로 서버에서 작동하는 페이지를 의미. (반응에 의한 변화가 없고 고정적인 HTML은 정적인 페이지에 해당한다.) 


서버 측의 스크립트(Server Side Script)다.

(서버 측에서 작업의 처리를 위해 사용하는 언어다. 반대되는 개념으로 클라이언트 사이드 스크립트로 대표주자 자바 스크립트가 있다. 자바스크립트는 '사용자' 쪽에서 실행되는 반면, ASP 는 서버 측에서 실행된다.)


서로 대화를 주고 받듯이 웹 페이지를 만들 수 있도록 MS사가 제공한 application

NT에 IIS(Internet Information Server)를 설치하므로써 사용 가능

IIS는 MS사에서 제공하는 웹 서버로, ASP는 IIS 3.0 이상에서만 동작

ASP 파일은 *.ASP 형태로 저장되어야 하며, 보통 서버 스크립트 언어를 사용

 서버 스크립트란?

ASP는 기존의 VBScript나 JavaScript와 크게 다를바 없는 스크립트입니다. 다만 '서버사이드 스크립트'라고 해서 서버에서 실행된다는 것이 다릅니다.

ASP에서 서버 스크립트를 구분하는 구분자는 <% 와 %>로, 이 사이에 사용된 스크립트는 서버에서 실행됩니다.

그렇기 때문에 브라우저에서 HTML 보기를 해도 ASP 코드 내용은 보이지 않습니다.

서버에서 실행되고 그 결과만을 가져다 보여주기 때문에 HTML 소스에는 그 결과값만 보이게 됩니다.


PHP와는 다르게 윈도우 운영체제에서만 작동한다는 단점이 있다. 정확하게는 마이크로소프트사의 NT머신 IIS3.0 이상에서만 동작하는 특별한 페이지이다.


작동원리

웹사이트 이용자가 어떠한 ASP페이지에 접속하면 이 ASP페이지는 ASP.DLL을 거처 해석된다음 HTML태그로 바뀌어진다. 그럼 웹브라우저가 이 HTML을 해석하여 화면에 보여주는것이다.



왜 ASP를 사용하나요

나모나 HTML로 홈페이지를 만들어 본 사람들은 어느 정도 시간이 지나면 자기의 생각을 홈페이지에서 마음대로 구현할 수 없다는 것에 실망을 하게 될 것입니다.

자바스크립트도 해 보고 플래쉬도 해 보지만 역시 만족스럽지 못합니다.

이 때 눈을 돌리게 되는 것이 ASP라고 할 수 있지요. 물론 대신에 CGI나 PHP라는 말도 많이 듣게 되겠지만.....그러면 사람들은 왜 ASP를 사용하고 있을까요?

 

1) 사용자와의 동적인 상호작용을 원하기 때문입니다.

HTML은 기본적으로 정보를 보여주기만 합니다. 하이퍼링크를 통해 사용자가 요청을 하면 웹서버가 그 요청에 응답하여 원하는 웹 페이지를 보여주는 방식으로 작업이 진행되지요.

그것도 일종의 상호작용이라고 할 수는 있겠지만 웹 서버를 운영하는 사람과 사용자가 서로 어떤 정보를 주고 받는다든지 아니면 사용자들간에 서로 정보를 주고 받는다든지 하는 일은 기본적으로 불가능합니다. ASP는 HTML로 불가능한 이런 일들을 가능하게 해 줍니다.

 

2) 서버측 자원을 사용해야 하는 경우입니다.

사용자로부터 어떤 내용을 입력받아 데이터베이스에 저장해야 할 경우와 같은 건데 이럴 경우는 반드시 ASP를 사용해야만 합니다.

예를 들어 게시판 작성, 파일 업로드, ID 인증 등과 같이 서버에 어떤 정보를 저장하거나 저장된 정보를 이용해 일을 하는 경우에는 ASP를 사용해야만 합니다.

왜냐하면 HTML은 원칙적으로 클라이언트에서 번역되는 문서이기 때문에 서버의 다른 자원에 접근할 수가 없기 때문입니다

 

3) 스크립트의 안정적인 실행을 위해서입니다.

자바스크립트를 예를 들어 볼까요? 자바스크립트도 날이 갈수록 버전이 높아지고 있습니다. v1.0, v1.1, v1.2, v1.3... 그러다보면 상위 버전의 자바스크립트를 삽입한 HTML문서가 어떤 웹 브라우저에서는 의도하는대로 번역이 되지 않는 사태가 벌어질 수도 있습니다. 이럴 경우 서버에서 실행되는 스크립트라면 서버에서 HTML형태로 번역된 후 넘겨지게 되니까 어떤 웹 브라우저에서라도 잘못될 염려가 없을 것입니다.

 

4) 스크립트 소스를 감추기 위해서입니다.

클라이언트측 스크립트들은 HTML 속에 포함되어 서버에서 클라이언트로 전송된 다음 클라이언트의 웹 브라우저에서 번역됩니다. 따라서 사용자들은 소스보기를 통해 스크립트의 내용을 볼 수가 있습니다.

그러나, 서버측 스크립트를 사용함으로써 개발자는 자신의 독특한 알고리즘을 감출 수가 있습니다. 물론 이런 목적을 위해 ASP를 사용한다는 것을 좋다고 할 수는 없겠지만 이런 기술로 밥먹고 사는 사람들에게는 꼭 필요한 것이 아닐까요?   

 



'프로그래밍 > ASP' 카테고리의 다른 글

ASP란 뭘까..  (0) 2016.06.20
IIS 및 ASP 사용 환경 구성  (0) 2016.06.20
ASP란 무엇인가  (0) 2016.02.20
Posted by GENESIS8

댓글을 달아 주세요

출처 : 위키백과


멀티스레딩(Multithreading) 컴퓨터는 여러 개의 스레드를 효과적으로 실행할 수 있는 하드웨어 지원을 갖추고 있다. 이는 스레드가 모두 같은 주소 공간에서 동작하여 하나의 CPU 캐시 공유 집합과 하나의 변환 색인 버퍼 (TLB)만 있는 멀티프로세서 시스템 (멀티 코어시스템)과는 구별한다. 그러므로 멀티스레딩은 프로그램 안에서 병렬 처리의 이점을 맛볼 수 있지만 멀티프로세싱 시스템은 여러 개의 프로그램들을 병렬로 처리할 수 있다. 멀티프로세싱 시스템이 여러 개의 완전한 처리 장치들을 포함하는 반면 멀티스레딩은 스레드 수준뿐 아니라 명령어 수준의 병렬 처리에까지 신경을 쓰면서 하나의 코어에 대한 이용성을 증가하는 것에 초점을 두고 있다.


장점[편집]

  1. 응답성 : 대화형 프로그램을 멀티스레드화하면, 프로그램의 일부분(스레드)이 중단되거나 긴 작업을 수행하더라도 프로그램의 수행이 계속되어, 사용자에 대한 응답성이 증가된다. 예를 들어, 멀티스레드가 적용된 웹 브라우저 프로그램에서 하나의 스레드가 이미지 파일을 로드하고 있는 동안, 다른 스레드에 사용자와의 상호 작용이 가능하다.
  2. 자원 공유 : 스레드는 자동적으로 그들이 속한 프로세스의 자원들과 메모리를 공유한다. 코드 공유의 이점은, 한 응용 프로그램이 같은 주소 공간 내에 여러 개의 다른 활동성 스레드를 가질 수 있다는 점이다.
  3. 경제성 : 프로세스 생성에 메모리와 자원을 할당하는 것은 비용이 많이 든다. 스레드는 자신이 속한 프로세스의 자원들을 공유하기 때문에, 스레드를 생성하고 문맥교환을 하는 편이 보다 경제적이다.
  4. 멀티프로세서 활용 : 멀티프로세서 구조에서는 각각의 스레드가 다른 프로세서에서 병렬로 수행될 수 있다. 단일 스레드 프로세스는 CPU가 많아도 CPU 한개에서만 실행된다. 즉, 다중 스레드화를 하면 다중 CPU에서 병렬성이 증가된다.

단점[편집]

  • 다중 스레드는 캐시나 변환 색인 버퍼(TLB)와 같은 하드웨어 리소스를 공유할 때 서로를 간섭할 수 있다.
  • 하나의 스레드만 실행 중인 경우 싱글 스레드의 실행 시간이 개선되지 않고 오히려 지연될 수 있다.
  • 멀티스레딩의 하드웨어 지원을 위해 응용 프로그램과 운영 체제 둘 다 충분한 변화가 필요하다.
  • 스레드 스케줄링은 멀티스레딩의 주요 문제이기도 하다.


멀티스레드 모델[편집]

다-대-일(Many-to-One)[편집]

여러 개의 사용자 수준 스레드들이 하나의 커널 스레드(프로세스)로 매핑되는 방식으로, 사용자 수준에서 스레드 관리가 이루어진다. 주로 커널 스레드를 지원하지 않는 시스템에서 사용하며, 한 번에 하나의 스레드만이 커널에 접근할 수 있다는 단점이 있다. 하나의 스레드가 커널에 시스템 호출을 하면 나머지 스레드들은 대기해야 하기 때문에 진정한 의미의 동시성을 지원하지 못한다. 다시 말해, 여러 개의 스레드가 동시에 시스템 호출을 사용할 수 없다. 또한 커널 입장에서는 프로세스 내부의 스레드들을 인식할 수 없고 하나의 프로세스로만 보이기 때문에 다중처리기 환경이라도 여러 개의 프로세서에 분산하여 수행할 수 없다.

일-대-일(One-to-One)[편집]



사용자 스레드들을 각각 하나의 커널 스레드로 매핑시키는 방식이다. 사용자 스레드가 생성되면 그에 따른 커널 스레드가 생성되는 것이다. 이렇게 하면 다-대-일 방식에서 시스템 호출 시 다른 스레드들이 중단되는 문제를 해결할 수 있으며 여러 개의 스레드를 다중처리기에 분산하여 동시에 수행할 수 있는 장점이 있다. 그러나 커널 스레드도 한정된 자원을 사용하므로 무한정 생성할 수는 없기 때문에, 스레드를 생성할 때 그 개수를 염두에 두어야 한다.

다-대-다(Many-to-Many)[편집]

여러 개의 사용자 스레드를 여러 개의 커널 스레드로 매핑시키는 모이다. 다-대-일 방식과 일-대-일 방식의 문제점을 해결하기 위해 고안되었다. 커널 스레드는 생성된 사용자 스레드와 같은 수 또는 그 이하로 생성되어 스케줄링한다. 다-대-일 방식에서 스레드가 시스템 호출시 다른 스레드가 중단되는 현상일-대-일 방식에서 사용할 스레드의 수에 대해 고민하지 않아도 된다. 커널이 사용자 스레드와 커널 스레드의 매핑을 적절하게 조절한다.







Posted by GENESIS8

댓글을 달아 주세요

Thread-Package Architectures 는 다음의 구조

user-level threads (1-on-N)

kernel supported threads (1-on-1)

multi-level (userland/kernel hybrid) threads (M-on-N)


[User-level threads]

User-Level threads는 응용 프로그램과 Link/Load가 되는 라이브러리로 구현되어집니다. 이 라이브러리에 동기화, 스케줄링 기능을 모두 담고 있습니다. 커널에서는 아무런 지원을 해주지 않으며, 커널이 보기에는 단지 그냥 Single process일 뿐입니다. 프로세스마다 런타임 라이브러리의 Copy가 호출되므로 스케줄링 정책을 프로세스마다 달리 취할 수 있으며, 각 Thread마다 time quantum을 소모할 필요 없고, 런타임 라이브러리가 context를 유지하기때문에 switching을 할 필요가 없습니다. 그래서 User-Level Threads는 빠르고, 매우 효율적입니다. 그러나 장애가 꽤 있습니다.

1. Blocking System Calls
Blocking function이란 처리가 완료되지않으면 return되지 않는 함수인데, 만약 특정 Thread에서 Blocking이 되어 버리면, 전체 process가 Blocking이 되어버립니다. 이런 이유로 운영체제가 제공하는 non-blocking 함수들만 사용해야 하며, 사용 빈도가 높은 함수(read,select,wait,...)들은 해당 함수의 non-blocking 버젼으로 대체해야할 필요가 있습니다. 
2. Shared System Resources
동기화나 Locking없이 Thread끼리 공유하는 변수(드러나지 않고 감춰져 있는 경우)가 있을때, 그 Thread가 thread-safe하지 않으면 Overwrite되는 문제가 생길 수 있습니다. 이 이유로 사용할 함수는 재진입이 가능해야합니다. User-Level뿐만아니라 Kernel-Level 함수까지 모두.
3. signal Handling, Thread Scheduling
User-Level에서 이것을 구현하기란 상당히 어렵습니다. Timeslice를 다루기 위해 Hardware Clock 인터럽트를 보통의 방법으로는 받지 못합니다. 선점형(Preemptive) 스케줄링을 하기 위해서는 커널로 부터 Time Siganl을 받는 함수를 등록해두어야 하며, Timer Alarm Siganl을 다루는것은 다른 시그널을 다루는 것보다 아주 어렵습니다.


4. Multiprocess Utilization
하나의 프로세스에서 Time을 공유하고 있기때문에 여러개의 CPU를 동시에 사용할 수는 없습니다.


정리 - 구현상의 어려움과 복잡성 그리고, 몇가지 장애에도 불구하고, concurrency와 efficiency의 이득을 가져다 줍니다.


[Kernel-level threads]

Kernel-level에 있는 Threads는 독립적으로 스케줄되므로 특정 Thread에서의 Blocking이 process로 전파되지 않습니다. 그래서 Blocking System Calls를 이용할수 있습니다. 또한 각 Threads끼리 Signal을 주고 받을 수 있습니다. 
Kernel-level threads는 특별히 고려할만한 장애를 가지고 있지는 않습니다. 물론 마찬가지로 Thread-Safe해야 하지만, OS 개발자들은 대개의 표준 라이브러리를 Thread-Safe하게(재진입해도 문제없겠끔) 만들기에 User-level threads보다 통상적으로 보다 덜 말썽을 피웁니다.

Kernel-level threads는 안정성에 비해서 너무 느리다는게 큰 단점입니다. 바로 Thread Context-Switch 때문입니다. 마로비치의 연구에 의하면 10배정도 느리다고 합니다.


[multiplexed threads]

User-level threads와 Kernel-level threads를 섞은 방법입니다. User-level thread(줄여서 Thread)는 LWP(가벼워진 프로세스, Lightweight Processes)에 의해 multiplex됩니다. 커널은 LWP를 스케줄링/실행하고, LWP는 대기중인 thread를 골라서 실행합니다. thread는 하나의 LWP에서 실행이 되어지며, Time slice가 바뀔때 LWP도 바뀌어질 수 있습니다. 프로그래머는 thread를 사용할 수도 있고, LWP를 직접 사용할수 있고, 둘 모두를 동시에 사용할 수도 있습니다.

User-level threads처럼 작동하면서 Hardware Parallelism과 Blocking calls에 대처할 수 있으며, Context-Switch을 많이 하지 않습니다.

이것의 장애는, LWP가 Blocking이 되면 이 LWP가 가진 몇개의 Thread도 동시에 Blocking이 됩니다. 또한 LWP의 Context-Switch 비용은 Kernel-level threas보다 결코 싸지 않습니다. 또한 다중 CPU에서 효율적일려면 각 Thread는 각각 다른 LWP에 할당되어 있어야 합니다. 각 Thread의 LWP 할당과 LWP의 CPU 할당은 별개로서 이루어지기 때문에, 각 Thread가 서로다른 CPU에 할당될려면 이렇게 작동하겠끔 하는 매커니즘이 따로 존재하여야합니다.

이러한 이유로 인해 LWP에 의한 multiplexed threads는 궁극의 해결책은 못됩니다. 이상적으로, Kernel-level threads의 결정적인 단점인 Thread Context-Switch를 user-level threds만큼 빠르게만 한다면 그것으로서 모든 장애는 해결됩니다. 물론 이 방법은 있지만 여전히 문제점을 가지고 있습니다. 아래 방법입니다.

[Scheduler Activation(kernel-supported user-level threads)]

이 방법은 User-level threads들을 위한 특별한 지원을 kernel이 해주는겁니다. 그 부분은 Scheduler Activation이라 합니다. User-level threads 라이브러리는 커널에게 프로세스를 요구할때와 양도할때를 알려줍니다. 그러면 커널의 Scheduler Activation은 이것을 커널에 의한 프로세스 주소 공간으로 할당된 Virtual Processor로 표현합니다. 여기서 스케줄링과 Blocking 감지가 이루어지며, Granted processor,Preemptive thread,Blocked,Unblocked등등의 Event를 User-level의 런타임 시스템에게 알려줍니다.

개념적으로 이 방법이 가장 확실합니다만 이것은 단 한가지, 그러나 치명적일 수도 있는, 약점이 있습니다.

커널과 라이브러리 코드가 효율적인 교신을 위해 같은 주소 공간을 공유하기 때문에 예외적으로 높은 신뢰를 가지고 있어야 하며 bug-free이어야합니다. 이 방법은 개념적으로 비정상적인 작동(의도적인 비정상적 작동 포함)과 버그에 대해서 강건함(robust)을 가지지 못합니다.

SunOS는 multiplexed threads를 지원합니다. 그외(WindowsNT,MACH,OS/2)는 Kernel-level threads를 지원합니다. 이 책에서는 MACH가 Kernel-level threads를 지원한다고 되어 있지만 어떤 Web site에 가니 MACH도 LWP도 지원하는걸로 되어 있군요. 이것이 multiplexed threads를 지원한다라는건지 아님 User-level threads의 구현상의 한방법인지는 모르겠습니다. 어쩌면 둘은 말만 다를뿐 별로 차이가 없는 것일지도 모르겠고.

Linux에서 작동되는 User-level threads는 아래와 같습니다.
Bare-Bones Threads,CLthreads,DCEthreads,FSU Pthreads,JKthread,LinuxThreads,LWP,NThreads (Numerical Threads),PCthreads,Provenzano Pthreads,QuickThreads,RexThreads.


http://www.sftw.umac.mo/resource/LDP/FAQ/Threads-FAQ/ThreadLibs.html



https://kldp.org/node/295#comment-764


Quote:
사용자 수준은 문맥교환(context switching)의 오버해드가 없다.

사용자 쓰레드 방식이 커널 쓰레드보다 오버헤드가 적은(없지는 않음) 이유는
쓰레드간 전환할 때마다 커널 스케줄러를 호출할 필요가 없기 때문입니다.
커널 스케줄러로 진입하려면 프로세서 모드를 사용자 모드에서 커널 모드로
전환해야 하는데, 이때 사용자쪽 하드웨어 레지스터를 전부 저장시키고, 커널
레지스터를 복구하고, 기타 등등...의 수많은 작업이 밑에서 일어납니다.
따라서 사용자 모드와 커널 모드를 많이 왔다갔다 할 수록 성능은 급격하게
떨어지는 것이죠. 사용자 쓰레드는 쓰레드 스케줄러가 사용자 모드에만 있기
때문에 그런 오버헤드는 발생하지 않습니다.

Quote:
커널 수준은 사용자 수준 보다 효율적일 수 있다.

그런데 사용자 쓰레드의 결정적인 단점은 프로세스내의 한 쓰레드가 커널로
진입하는 순간 나머지 쓰레드들도 전부 정지된다는 점입니다. 커널이 쓰레드의
존재를 알지 못하므로 불가피한 현상입니다. 쓰레드가 커널로 진입할 때는
write(), read(), ...같은 시스템 호출을 부를 때인데, 시스템 호출 길이가
짧아서 바로 리턴할 때는 문제가 없지만 연산이 길어지면 상당한 문제
됩니다. 전체 프로세스의 응답성이 확~ 떨어지죠.

그리고 커널 쓰레드를 쓰면 멀티프로세서를 활용할 수 있다는 큰 장점
있습니다. 사용자 쓰레드는 CPU가 아무리 많더라도 커널 모드에서 쓰레드
단위로 스케줄이 안되므로 각 CPU에 효율적으로 쓰레드를 배당할 수 없는
문제가 있습니다(프로세스 단위로만 배당이 되므로).

그래서 리눅스, 윈도, 솔라리스를 비롯한 대부분의 운영체제는 사용자 쓰레드를
쓰지 않고 커널 쓰레드, 또는 커널/사용자 쓰레드 혼합 방식을 쓰는 추세입니다.

Quote:
그리고 현재 리눅스에서는 두가지 쓰레드 모델이 모두 지원 되나요?

예, 모두 지원됩니다.

쓰레드 방식은 크게 세가지가 있습니다. 이중 사용자 쓰레드는 요즘들어 
리눅스에서는 별로 쓰는 사람이 없는 것 같고, 1-on-1 커널 쓰레드 방식인
glibc내의 LinuxThreads가 가장 널리 쓰입니다. 최근에는 이걸 더 발전시킨
NPTL(Native POSIX Threading Library)가 활발히 개발중에 있습니다. 
커널/사용자 쓰레드 혼합 방식인 NGPT(Next Generation POSIX Threads)
도 IBM의 지원하에 최근 2.2.0까지 나와 있습니다. 둘간의 차이를 여기서
설명하기엔 좀 긴 것 같고, 전자가 후자보다 훨씬 간단한 반면, 후자의 성능이
전자보다 다소 높은 것으로 알려져 있습니다. 여담입니다만, NPTL이 처음
나왔을 때 우리 것이 NGPT보다도 성능이 훨씬 좋다고 자랑(?)을 했더니
NGPT팀이 자극을 받았나 봅니다. NGPT 홈페이지에 가면 새버전(2.2.0)
성능이 LinuxThreads는 물론 NPTL도 능가한다고 큼지막하게 써놨습니다.

Quote:
kernel thread는 user thread에서 LWP 자료구조를 통해 연결되는데

중요한 것은 아닙니다만...

LWP는 경량 프로세스(lightweight process)의 준말로, 솔라리스에서(그리고
최근의 NetBSD에서) 내부적으로 쓰는 말입니다. 리눅스, 윈도, FreeBSD에선
그냥 thread라고 합니다.


http://mesl.khu.ac.kr/?document_srl=271099


커널쓰레드는 커널단위로 쓰레드가 할당되기 때문에 운영체제가 각 커널 쓰레드 목록을 관리하구요

유저 레벨 쓰레드는 말그대로 프로세스 안에 유저단위로 쓰레드가 움직이는 양상이기때문에 
운영체제입장에서 보면 결국은 하나의 프로세스가 모든 쓰레드를 관리하는 모양이 됩니다.
(운영체제가보면 프로세스는 하나가 돌고있는거라고 볼수 있겠네요)
그렇기 때문에 만일 그 프로세스 내에서 문제가 생겨서 프로세스에러가 생긴다면
유저레벨 쓰레드에서는 프로세스의 망가짐으로 인해 모든 유저가 큰 타격을 받을수가 있습니다. 하지만 커널모드쓰레드는 각 커널이 개별적이라 그런 위험부문에선 안정적이라고 볼 수도 있겠네요

더 붙여 말하자면 user-level thread는 커널자체에서 개념을 바꾸기 힘들기 때문에 임시적으로 사용하는 방법이었고, 추후에 나오는 커널들에서 완벽한 스레드를 지원하는 kernel-level thread로 바뀌었다


http://dakuo.tistory.com/93




유저 영역 : 사용자가 구현한 프로그램 동작시 사용하게 되는 메모리 영역

커널 영역 : 운영체제 동작시 사용하게 되는 메모리 영역


커널 레벨 쓰레드 :

쓰레드를 생성 및 스케줄링하는 주체가 커널


장점 : 안전성과 다양한 기능

단점 : 유저 모드와 커널 모드로의 전환으로 인해 성능이 저하된다.

(참고 : 사용자가 구현한 프로그램은 기본적으로 유저모드에서 동작하다가 Windows 커널이 실행되어야 하는 경우
커널 모드로의 전환이 일어나고 일을 마치면 다시 유저 모드로 전환된다)


유저 레벨 쓰레드 :

커널에 의존적이지 않은 형태로 쓰레드의 기능을 제공하는 라이브러리를 활용

(참고 : 스케줄러는 쓰레드를 지원하지 않아 쓰레드의 존재를 모른다. 따라서 프로세스 단에서 스케줄링을 한다
따라서 쓰레드 끼리의 스케줄링은 유저가 구현해야 한다) 

장점 : 유저모드에서 커널모드로의 전환이 필요없다. 성능이 좋다

단점 : 프로그래밍 하기 어렵고 커널 레벨 쓰레드에 비해 결과 예측이 어렵다.



http://hyunbogi.tistory.com/88







Posted by GENESIS8

댓글을 달아 주세요

  1. 2020.06.28 23:33  댓글주소  수정/삭제  댓글쓰기

    비밀댓글입니다