요청 받기 컬 PHP의 예. cURL에서 GET 요청 보내기

이 문서에서는 귀하가 네트워킹과 HTML의 기본 사항을 알고 있다고 가정합니다.

좋은 컴퓨터 시스템을 구축하려면 스크립트 작성 능력이 필수적입니다. 쉘 스크립트와 자동화된 명령을 실행하는 다양한 프로그램을 갖춘 Unix 시스템의 확장성은 Unix 시스템이 성공한 이유 중 하나입니다.

웹으로 이동하는 애플리케이션의 수가 증가함에 따라 HTTP 스크립트에 대한 주제가 점점 대중화되고 있습니다. 이 영역의 중요한 작업은 인터넷에서 자동으로 정보를 검색하고, 웹 서버로 데이터를 전송하거나 다운로드하는 것입니다.

Curl은 다양한 종류의 URL 조작 및 전송을 수행할 수 있는 명령줄 도구입니다. 이 문서에서는 간단한 HTTP 요청을 만드는 데 중점을 둡니다. 입력할 위치를 이미 알고 있다고 가정합니다.

# 컬 --help

# 컬 --수동

컬에 대한 정보를 얻으려면.

Curl은 모든 것을 대신해 주는 도구가 아닙니다. 요청을 생성하고, 데이터를 수신하고, 데이터를 보냅니다. 모든 것을 하나로 묶으려면 일종의 "접착제"가 필요할 수 있습니다. 아마도 스크립트 언어(예: bash)나 수동 호출이 필요할 수 있습니다.

1. HTTP 프로토콜

HTTP는 웹 서버로부터 데이터를 수신할 때 사용되는 프로토콜입니다. 이는 TCP/IP를 기반으로 구축된 매우 간단한 프로토콜입니다. 또한 이 프로토콜을 사용하면 나중에 설명하겠지만 여러 가지 방법을 사용하여 클라이언트에서 서버로 정보를 보낼 수 있습니다.

HTTP는 어떤 작업을 요청하기 위해 클라이언트에서 서버로 전송되는 ASCII 텍스트 문자열입니다. 요청을 받으면 서버는 여러 서비스 텍스트 줄로 클라이언트에 응답한 다음 실제 콘텐츠로 응답합니다.

컬 -v 스위치를 사용하면 컬이 서버에 보내는 명령과 기타 정보 텍스트를 볼 수 있습니다. -v 스위치는 아마도 컬과 웹 서버 간의 상호 작용을 디버그하거나 이해하는 유일한 방법일 것입니다.

2. URL

URL 형식(Uniform Resource Locator)은 인터넷의 특정 리소스 주소를 지정합니다. 아마도 알고 계실 것입니다. 예를 들어 URL은 http://curl.haxx.se 또는 https://yourbank.com입니다.

3. 받기(GET) 페이지

가장 간단하고 일반적인 HTTP 요청은 URL의 콘텐츠를 가져오는 것입니다. URL은 웹페이지, 이미지 또는 파일에 연결될 수 있습니다. 클라이언트는 서버에 GET 요청을 보내고 요청된 문서를 받습니다. 명령을 실행하면

# 컬 http://curl.haxx.se

터미널 창에 웹 페이지 출력이 표시됩니다. 이 URL에 포함된 전체 HTML 문서입니다.

모든 HTTP 응답에는 일반적으로 숨겨져 있는 헤더 세트가 포함되어 있습니다. 문서 자체와 함께 이를 보려면 컬 -i 옵션을 사용하십시오. -I 스위치를 사용하여 헤더만 요청할 수도 있습니다(이렇게 하면 컬이 HEAD 요청을 강제로 수행하게 됨).

4. 모양

양식은 사용자가 데이터를 입력하고 "확인" 또는 "제출" 버튼을 클릭한 후 데이터가 서버로 전송되는 필드가 포함된 HTML 페이지로 웹 사이트를 표시하는 기본 방법입니다. 그러면 서버는 수신된 데이터를 사용하여 다음에 무엇을 할지 결정합니다. 데이터베이스에서 정보를 조회하거나, 입력한 주소를 지도에 표시하거나, 오류 메시지를 추가하거나, 해당 정보를 사용하여 사용자를 인증합니다. 물론 서버 측에는 데이터를 수신하는 일부 프로그램이 있습니다.

4.1 받기

GET 양식은 다음과 같이 GET 메서드를 사용합니다.

브라우저에서 이 코드를 열면 텍스트 필드와 "확인"이라는 버튼이 있는 양식이 표시됩니다. "1905"를 입력하고 확인을 클릭하면 브라우저는 따라갈 새 URL을 생성합니다. URL은 이전 URL의 경로와 "junk.cgi?birthyear=1905&press=OK"와 같은 문자열로 구성된 문자열로 표시됩니다.

예를 들어, 양식이 "www.hotmail.com/when/birth.html"에 있는 경우 확인을 클릭하면 "www.hotmail.com/when/junk.cgi?birthyear=1905&press=OK" URL로 이동됩니다. " .

대부분의 검색 엔진은 이런 방식으로 작동합니다.

컬이 GET 요청을 생성하도록 하려면 양식에서 예상한 내용을 입력하기만 하면 됩니다.

# 컬 "www.hotmail.com/when/junk.cgi?birthyear=1905&press=OK"

4.2 포스트

GET 메소드를 사용하면 입력된 모든 정보가 브라우저의 주소 표시줄에 표시됩니다. 이는 페이지를 북마크해야 할 때 유용할 수 있지만 양식 필드에 민감한 정보를 입력하거나 필드에 입력된 정보의 양이 너무 많아(읽을 수 없는 URL이 발생하는 경우) 명백한 단점이 됩니다.

HTTP 프로토콜은 POST 메서드를 제공합니다. 이를 통해 클라이언트는 URL과 별도로 데이터를 전송하므로 주소 표시줄에 해당 데이터가 표시되지 않습니다.

POST 요청을 생성하는 양식은 이전 양식과 유사합니다.

Curl은 다음과 같은 데이터를 사용하여 POST 요청을 생성할 수 있습니다.

# 컬 -d "birthyear=1905&press=%20OK%20" www.hotmail.com/when/junk.cgi

이 POST 요청은 가장 널리 사용되는 방법인 "Content-Type application/x-www-form-urlencoded"를 사용합니다.

서버에 보내는 데이터는 올바르게 인코딩되어야 하며, 컬은 이를 수행하지 않습니다. 예를 들어 데이터에 공백이 포함되도록 하려면 공백을 %20 등으로 바꿔야 합니다. 이 문제에 대한 관심 부족은 데이터가 제대로 전송되지 않는 일반적인 실수입니다.

1995년에 HTTP를 통해 데이터를 전송하는 추가 방법이 정의되었습니다. 이는 RFC 1867에 문서화되어 있으므로 이 방법을 RFC1867 게시라고도 합니다.

이 방법은 주로 파일 업로드를 더 잘 지원하도록 설계되었습니다. 사용자가 파일을 업로드할 수 있는 양식은 HTML에서 다음과 같습니다.

Content-Type은 multipart/form-data로 설정되어 있습니다.

컬을 사용하여 이 양식으로 데이터를 보내려면 다음 명령을 입력하십시오.

# 컬 -F 업로드=@localfilename -F 프레스=확인

4.4 숨겨진 필드

HTML 애플리케이션에서 상태 정보를 전달하는 일반적인 방법은 양식에서 숨겨진 필드를 사용하는 것입니다. 숨겨진 필드는 채워지지 않으며 사용자에게 표시되지 않으며 일반 필드와 동일한 방식으로 전송됩니다.

하나의 표시 필드, 하나의 숨겨진 필드 및 확인 버튼이 있는 양식의 간단한 예:

컬을 사용하여 POST 요청을 보내려면 해당 필드가 숨겨져 있는지 여부를 생각할 필요가 없습니다. 컬의 경우 모두 동일합니다.

# 컬 -d "생년월일=1905&press=OK&사람=다니엘"

4.5 POST 요청이 어떤 모습인지 알아보세요

양식을 작성하고 컬을 사용하여 서버에 데이터를 보내려는 경우 POST 요청이 브라우저를 사용하여 수행한 것과 정확히 동일하게 보이도록 할 수 있습니다.

POST 요청을 보는 쉬운 방법은 양식이 포함된 HTML 페이지를 디스크에 저장하고 방법을 GET으로 변경한 후 "제출" 버튼을 클릭하는 것입니다(데이터가 전송될 URL도 변경할 수 있음).

GET 양식을 사용할 때 예상한 대로 "?" 문자로 구분된 URL에 첨부된 데이터를 볼 수 있습니다.

5. 넣어

아마도 HTTP 서버에 데이터를 업로드하는 가장 좋은 방법은 PUT을 사용하는 것입니다. 다시 말하지만, 이를 위해서는 수행할 작업과 HTTP PUT 스트림을 수락하는 방법을 아는 백엔드의 프로그램(스크립트)이 필요합니다.

컬을 사용하여 서버에 파일을 보냅니다.

# 컬 -T 업로드 파일 www.uploadhttp.com/receive.cgi

6. 인증

인증 - 서버에 사용자 이름과 비밀번호를 전달한 후 필요한 요청을 완료할 수 있는 권한이 있는지 확인합니다. 기본 인증(curl이 기본적으로 사용함)은 일반 텍스트를 기반으로 합니다. 즉, 사용자 이름과 비밀번호는 암호화되지 않고 Base64 알고리즘으로 약간만 "포그"되므로 사용자와 사용자 사이의 경로에 있는 공격자가 다음과 같은 행위를 할 수 있습니다. 이 정보 HTTP 서버를 찾으십시오.

컬에게 사용자 이름과 비밀번호를 사용하도록 지시:

# 컬 -u 이름:비밀번호 www.secrets.com

사이트에서는 다른 인증 방법(서버가 헤더에 쓰는 내용 확인)을 사용해야 할 수 있습니다. 이 경우 --ntlm, --digest, --negotiate 또는 심지어 --anyauth 스위치를 사용할 수 있습니다. 때로는 외부 HTTP 서버에 대한 액세스가 프록시를 통해 발생하는 경우가 있는데, 이는 회사와 기업에서 흔히 수행됩니다. HTTP 프록시는 인터넷에 액세스하기 위해 자신의 사용자 이름과 비밀번호가 필요할 수 있습니다. 해당 컬 키:

# 컬 -U 프록시사용자:프록시암호 컬.haxx.se

프록시에 NTLM 방법을 사용한 인증이 필요한 경우 --proxy-ntlm을 지정하고, Digest 방법인 경우 --proxy-digest를 지정합니다.

-u 및 -U 옵션에 비밀번호를 지정하지 않으면 컬이 대화식으로 비밀번호를 묻습니다.

컬이 실행 중일 때 시작 문자열(및 키와 비밀번호 포함)이 작업 목록에서 시스템의 다른 사용자에게 표시될 수 있습니다. 이를 방지하는 방법이 있습니다. 이에 대한 자세한 내용은 아래에서 확인하세요.

7. 추천인

HTTP 요청에는 사용자가 이 리소스를 방문한 URL을 나타내는 "참조자" 필드가 포함될 수 있습니다. 일부 프로그램/스크립트는 "참조자" 필드를 확인하고 사용자가 알 수 없는 페이지에서 온 경우 요청을 실행하지 않습니다. 비록 이것이 어리석은 확인 방법임에도 불구하고 많은 스크립트가 여전히 이를 사용하고 있습니다. 컬을 사용하면 "참조자" 필드에 무엇이든 넣을 수 있으므로 원하는 대로 작동하게 할 수 있습니다.

이는 다음과 같이 수행됩니다.

# 컬 -e http://curl.haxx.se daniel.haxx.se

8. 사용자 에이전트

모든 HTTP 요청은 사용자의 클라이언트 애플리케이션을 지정하는 "User-Agent" 필드를 지원합니다. 많은 웹 애플리케이션은 이 정보를 사용하여 어떤 방식으로든 페이지를 렌더링합니다. 웹 프로그래머는 모양을 개선하고 다양한 javascript, vbscript 스크립트 등을 사용하기 위해 다양한 브라우저 사용자를 위해 여러 버전의 페이지를 만듭니다.

때로는 컬이 브라우저에서 본 것과 동일하지 않은 페이지를 반환하는 경우가 있습니다. 이 경우 "사용자 에이전트" 필드를 사용하여 다시 한 번 서버를 속이는 것이 적절합니다.

Windows 2000 시스템에서 컬을 Internet Explorer로 위장합니다.

# 컬 -A "Mozilla/4.0(호환; MSIE 5.01; Windows NT 5.0)"

Linux 시스템에서 Netscape 4.73이 되어보는 것은 어떨까요(PIII):

# 컬 -A "Mozilla/4.73 (X11; U; Linux 2.2.15 i686)"

9. 리디렉션

귀하의 요청에 응답할 때 서버는 페이지 자체 대신 원하는 페이지로 이동하기 위해 브라우저가 다음에 이동해야 하는 위치에 대한 표시를 반환할 수 있습니다. 브라우저로의 리디렉션을 나타내는 헤더는 "위치:"입니다.

기본적으로 컬은 "위치:"에 지정된 주소로 이동하지 않고 단순히 평소대로 페이지를 표시합니다. 하지만 다음과 같이 지시할 수 있습니다.

# 컬 -L www.sitethatredirects.com

컬을 사용하여 다른 페이지로 즉시 리디렉션되는 사이트에 대한 POST 요청을 수행하는 경우 -L 및 -d/-F 조합을 안전하게 사용할 수 있습니다. Curl은 첫 번째 페이지에 대해 POST 요청을 생성하고 다음 페이지에 대해 GET 요청을 생성합니다.

10. 쿠키

쿠키를 사용하면 웹 브라우저가 클라이언트 측의 상태를 제어합니다. 쿠키는 콘텐츠가 첨부된 이름입니다. 쿠키를 전송함으로써 서버는 다음에 쿠키를 전송해야 하는 경로와 호스트 이름, 쿠키의 수명 및 기타 매개변수를 클라이언트에게 알려줍니다.

클라이언트가 허용된 쿠키에 지정된 주소로 서버에 연결되면 클라이언트는 해당 쿠키를 서버에 보냅니다(수명 기간이 만료되지 않은 경우).

많은 애플리케이션과 서버는 이 방법을 사용하여 여러 요청을 하나의 논리적 세션으로 결합합니다. 컬이 이 기능을 수행하려면 브라우저처럼 쿠키를 저장하고 보낼 수 있어야 합니다.

컬을 사용하여 페이지를 수신할 때 서버에 쿠키를 보내는 가장 간단한 방법은 명령줄에 적절한 스위치를 추가하는 것입니다.

# 컬 -b "이름=다니엘" www.cookiesite.com

쿠키는 일반 HTTP 헤더로 전송됩니다. 이를 통해 컬은 헤더를 보존하여 쿠키를 저장할 수 있습니다. 컬을 사용하여 쿠키를 저장하는 작업은 다음 명령으로 수행됩니다.

# 컬 -D headers_and_cookies www.cookiesite.com

(그런데 쿠키를 저장하려면 -c 스위치를 사용하는 것이 더 좋습니다. 자세한 내용은 아래에서 설명합니다.)

컬에는 모든 기능을 갖춘 쿠키 핸들러가 있는데, 이는 서버에 다시 연결하고 지난번에 저장한(또는 수동으로 수정한) 쿠키를 사용하려는 경우에 유용합니다. 파일에 저장된 쿠키를 사용하려면 다음과 같이 컬을 호출하십시오.

# 컬 -b 저장된_쿠키_인_파일 www.cookiesite.com

-b 스위치를 지정하면 Curl의 "쿠키 엔진"이 활성화됩니다. 컬이 쿠키만 허용하도록 하려면 -b를 사용하여 존재하지 않는 파일을 지정하세요. 예를 들어, 컬이 페이지에서 쿠키를 수락한 다음 리디렉션을 따르도록 하려면(아마도 방금 수락한 쿠키를 제공할 수도 있음) 다음과 같이 컬을 호출할 수 있습니다.

# 컬 -b nada -L www.cookiesite.com

Curl은 Netscape 및 Mozilla 형식의 쿠키를 읽고 쓸 수 있습니다. 이는 브라우저와 자동 스크립트 간에 쿠키를 교환하는 편리한 방법입니다. -b 스위치는 주어진 쿠키가 지정된 브라우저의 쿠키인지 여부를 자동으로 감지하고 그에 따라 처리하며, -c/--cookie-jar 스위치를 사용하면 작업이 완료될 때 컬이 새 쿠키를 쓰도록 강제할 수 있습니다.

# 컬 -b 쿠키.txt -c newcookies.txt www.cookiesite.com

11. HTTPS

HTTP 전송을 보호하는 방법에는 여러 가지가 있습니다. 이 문제를 해결하는 가장 잘 알려진 프로토콜은 HTTPS, 즉 HTTP over SSL입니다. SSL은 네트워크를 통해 주고받는 모든 데이터를 암호화하므로 귀하의 정보가 기밀로 유지될 가능성이 높아집니다.

Curl은 무료 OpenSSL 라이브러리를 사용하여 HTTPS 서버에 대한 요청을 지원합니다. 요청은 일반적인 방식으로 발생합니다.

# 컬 https://that.secure.server.com

11.1 인증서

HTTPS 세계에서는 인증을 위해 사용자 이름과 비밀번호 외에 인증서도 사용합니다. Curl은 클라이언트측 인증서를 지원합니다. 모든 인증서는 컬이 작업을 시작하기 전에 입력해야 하는 암호로 잠겨 있습니다. 암호는 명령줄에서 지정하거나 대화식으로 입력할 수 있습니다. 컬의 인증서는 다음과 같이 사용됩니다.

# 컬 -E mycert.pem https://that.secure.server.com

Curl은 또한 서버 인증서를 로컬에 저장된 인증서와 비교하여 서버의 신뢰성을 확인합니다. 불일치가 있으면 컬은 연결을 거부합니다. 인증 확인을 무시하려면 -k 스위치를 사용하십시오.

인증서에 대한 자세한 내용은 http://curl.haxx.se/docs/sslcerts.html에서 확인할 수 있습니다.

12. 사용자 정의 요청 헤더

개별 컬 요청의 요소를 변경하거나 추가해야 할 수도 있습니다.

예를 들어 POST 요청을 PROPFIND로 변경하고 데이터를 "Content-Type: text/xml"(일반 Content-Type 대신)로 보낼 수 있습니다.

# 컬 -d " " -H "콘텐츠 유형: 텍스트/xml" -X PROPFIND url.com

내용 없이 제목을 지정하여 제목을 제거할 수 있습니다. 예를 들어 "Host:" 헤더를 제거하여 요청을 "비어 있게" 만들 수 있습니다.

# 컬 -H "호스트:" http://mysite.com

제목을 추가할 수도 있습니다. 서버에 "Destination:" 헤더가 필요할 수 있습니다.

# 컬 -H "대상: http://moo.com/nowhere" http://url.com

13. 디버깅

사이트가 브라우저 요청과 다르게 컬 요청에 응답하는 경우가 종종 있습니다. 이 경우 컬을 브라우저와 최대한 유사하게 만들어야 합니다.

  • --trace-ascii 스위치를 사용하여 쿼리에 대한 자세한 기록을 저장하면 쿼리를 자세히 조사하고 문제를 이해할 수 있습니다.
  • 쿠키를 확인하고 필요한 경우 사용하십시오(-b를 읽고 -c를 저장).
  • "user-agent" 필드에 널리 사용되는 최신 브라우저 중 하나를 지정하세요.
  • 브라우저와 마찬가지로 "참조자" 필드를 입력합니다.
  • POST 요청을 사용하는 경우 모든 필드가 브라우저와 동일한 순서로 전송되는지 확인하세요(위의 4.5 참조).

이 어려운 작업에 대한 좋은 도우미는 Mozilla/Firefox용 LiveHTTPHeader 플러그인입니다. 이 플러그인을 사용하면 이 브라우저가 보내고 받는 모든 헤더를 볼 수 있습니다(HTTPS를 사용하는 경우에도).

낮은 수준의 접근 방식은 ethereal 또는 tcpdump와 같은 프로그램을 사용하여 네트워크에서 HTTP 트래픽을 캡처한 다음 브라우저에서 수신하고 보낸 헤더를 분석하는 것입니다(HTTPS는 이 접근 방식을 효과적이지 않게 만듭니다).

RFC 2616은 HTTP 프로토콜을 이해하려는 사람이라면 누구나 읽어야 합니다.

RFC 2396은 URL 구문을 설명합니다.

RFC 2109는 쿠키 작동 방식을 정의합니다.

RFC 1867은 파일 업로드 포스트 형식을 정의합니다.

http://openssl.planetmirror.com - OpenSSL 프로젝트 홈 페이지

http://curl.haxx.se - cURL 프로젝트 홈페이지

지난 기사에서 우리는 당신과 논의했습니다. 그러나 때로는 스크립트가 허용하는 경우도 있습니다. GET 요청(보통 검색 스크립트입니다). 그리고 그러한 스크립트로부터 데이터를 처리하고 수신하려면 다음을 수행할 수 있어야 합니다. cURL에서 GET 요청 보내기이 글을 읽으면 당신도 그 점을 배우게 될 것입니다.

원칙 cURL에서 GET 요청 보내기와 완전 똑같아 POST 방식으로 보내기: 있다 소스 파일그리고 거기에 대상 파일. 모듈을 사용하는 소스 파일 곱슬 곱슬하다, GET 요청을 보낸다대상 파일에. 대상 파일은 이 요청을 처리하고 다시 기능을 사용하여 소스 파일에서 허용되는 결과를 반환합니다. 곱슬 곱슬하다.

모든 것을 명확하게 하기 위해 우리가 본 간단한 예를 살펴보겠습니다. POST 요청 보내기. 즉, 소스 파일은 두 개의 숫자를 보내고, 대상 파일은 이 숫자의 합을 반환하며, 소스 파일은 이를 받아 변수에 쓴 다음 브라우저에 출력합니다.

우선 대상 파일(" 수신기.php"):

$a = $_GET["a"];
$b = $_GET["b"];
에코 $a + $b;
?>

여기서는 모든 것이 매우 간단합니다. 변수를 작성합니다. $a그리고 $b데이터 GET 요청, 그런 다음 소스 파일에서 허용되는 합계를 표시합니다.

이제 파일 자체를 만들어 보겠습니다.

이 게시물에서는 cURL을 사용하는 방법, 사용 위치, 아직 이해하지 못한 경우 cURL을 이해해야 하는 이유를 예제와 함께 보여 드리겠습니다.

그것이 무엇인지, 무엇과 함께 먹는지 간략하게 설명하겠습니다.

Guzzle은 HTTP 요청 전송 프로세스를 단순화하기 위해 만들어졌습니다. 요청을 보내는 데 자주 사용됩니다. API그리고 일반적으로 무엇이든.

실제 예:

최근에 개발한 API가 있고 이제 작업을 시작할 시간입니다. 자신만의 라이브러리를 작성하거나 이를 사용하는 미니 클래스를 작성할 수도 있지만 (1) 시간이 많이 걸리고 그렇지 않더라도 솔루션이 최선이 아닐 가능성이 높습니다. ( 2) 지속적인 지원과 개선이 필요합니다. 이 경우에는 대규모 커뮤니티에서 지원되고 Guzzle 저장소에 이미 12,000개의 별이 있는 기성 솔루션을 사용하는 것이 좋습니다. 이는 매우 칭찬할만한 일입니다.

당신은 질문할 수 있습니다: 이미 많은 라이브러리가 있는데 이것이 왜 필요한가요?

Guzzle은 그 자체로 최고의 모든 것을 모아 더욱 향상시켰으며 이제 HTTP 요청 작업을 위한 가장 인기 있는 PHP 라이브러리입니다. 정말 멋지네요. 요청의 단순성을 살펴보세요.

// 기본 URI를 사용하여 클라이언트를 생성합니다. $client = new GuzzleHttp\Client(["base_uri" => "http://site/"]); // 이제 http://bologe로 요청을 보낼 수 있습니다. Guzzle은 기본 링크를 기억하므로 이제 편의를 위해 후속 페이지만 지정할 수 있습니다. $response = $client->request("GET", "about");

시원한? 좋아요.

Guzzle의 문서는 매우 광범위하여 모든 옵션을 설명하는 것은 불가능하며 전체 게시물이 필요하므로 곧 작성하겠습니다 :)

후문

궁금한 사항이 있는 경우 이 게시물에 아래 내용을 적어 주시면 기꺼이 도와드리겠습니다. 또한, 기사에 수정사항이 있거나 어딘가에 오류가 있거나 추가하고 싶은 내용이 있으면 언제든지 알려주시면 감사하겠습니다.

cURL은 URL 구문을 사용하여 파일과 데이터를 전송하도록 설계된 특수 도구입니다. 이 기술은 HTTP, FTP, TELNET 등 다양한 프로토콜을 지원합니다. cURL은 원래 명령줄 도구로 설계되었습니다. 다행스럽게도 cURL 라이브러리는 PHP 프로그래밍 언어에서 지원됩니다. 이 기사에서는 cURL의 고급 기능 중 일부를 살펴보고, 습득한 지식을 PHP를 사용하여 실제로 적용하는 방법도 다룰 것입니다.

왜 cURL을 사용하나요?

실제로 웹페이지 콘텐츠를 샘플링하는 데는 몇 가지 대체 방법이 있습니다. 많은 경우 주로 게으름 때문에 cURL 대신 간단한 PHP 함수를 사용했습니다.

$content = file_get_contents("http://www.nettuts.com"); // 또는 $lines = file("http://www.nettuts.com"); // 또는 readfile("http://www.nettuts.com");

그러나 이러한 기능은 사실상 유연성이 없으며 오류 처리 등의 측면에서 엄청난 단점을 포함하고 있습니다. 또한 쿠키 상호 작용, 인증, 양식 제출, 파일 업로드 등 이러한 표준 기능으로는 수행할 수 없는 특정 작업이 있습니다.

cURL은 다양한 프로토콜과 옵션을 지원하고 URL 요청에 대한 자세한 정보를 제공하는 강력한 라이브러리입니다.

기본 구조

  • 초기화
  • 매개변수 할당
  • 실행 및 결과 가져오기
  • 메모리 확보

// 1. 초기화 $ch = cur_init(); // 2. URL을 포함한 매개변수를 지정합니다.curl_setopt($ch, CURLOPT_URL, "http://www.nettuts.com"); 컬_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 컬_setopt($ch, CURLOPT_HEADER, 0); // 3. 결과로 HTML을 얻습니다. $output = cur_exec($ch); // 4. 연결을 닫습니다. 컬_close($ch);

2단계(즉,curl_setopt() 호출)는 다른 모든 단계보다 이 문서에서 훨씬 더 많이 논의될 것입니다. 이 단계에서는 알아야 할 가장 흥미롭고 유용한 모든 일이 발생합니다. cURL에는 가장 신중한 방법으로 URL 요청을 구성하기 위해 지정해야 하는 다양한 옵션이 있습니다. 우리는 전체 목록을 고려하지 않고 이 수업에 필요하고 유용하다고 생각하는 것에만 초점을 맞출 것입니다. 이 주제에 관심이 있다면 다른 모든 것을 직접 공부할 수 있습니다.

오류 검사

또한 조건문을 사용하여 작업이 성공적으로 완료되었는지 테스트할 수도 있습니다.

// ... $output = 컬_exec($ch); if ($output === FALSE) ( echo "cURL 오류: " .curl_error($ch); ) // ...

여기서는 매우 중요한 점을 지적해 주시기 바랍니다. 비교를 위해 "== false" 대신 "=== false"를 사용해야 합니다. 잘 모르는 사람들을 위해 이는 빈 결과와 오류를 나타내는 부울 값 false를 구별하는 데 도움이 됩니다.

정보 수신

또 다른 추가 단계는 cURL 요청이 실행된 후 해당 요청에 대한 데이터를 얻는 것입니다.

// ... 컬_exec($ch); $info = 컬_getinfo($ch); echo "찍었어요" . $info["total_time"] . "URL의 초". $info["url"]; //...

반환된 배열에는 다음 정보가 포함됩니다.

  • "URL"
  • "컨텐츠 타입"
  • "http_코드"
  • "헤더_크기"
  • "요청_크기"
  • "파일 시간"
  • "ssl_verify_result"
  • "리디렉션_카운트"
  • "총 시간"
  • "이름 조회_시간"
  • “연결_시간”
  • “사전 환승_시간”
  • “크기_업로드”
  • “크기_다운로드”
  • "속도_다운로드"
  • "속도_업로드"
  • “다운로드 콘텐츠_길이”
  • “업로드_콘텐츠_길이”
  • “starttransfer_time”
  • "리디렉션_시간"

브라우저에 따른 리디렉션 감지

첫 번째 예에서는 다양한 브라우저 설정을 기반으로 URL 리디렉션을 감지할 수 있는 코드를 작성하겠습니다. 예를 들어, 일부 웹사이트는 휴대폰이나 기타 장치의 브라우저를 리디렉션합니다.

우리는 CURLOPT_HTTPHEADER 옵션을 사용하여 사용자의 브라우저 이름과 사용 가능한 언어를 포함하여 나가는 HTTP 헤더를 정의할 것입니다. 결국 우리는 어떤 사이트가 우리를 다른 URL로 리디렉션하는지 확인할 수 있게 될 것입니다.

// URL 테스트 $urls = array("http://www.cnn.com", "http://www.mozilla.com", "http://www.facebook.com"); // 브라우저 테스트 $browsers = array("standard" => array ("user_agent" => "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5 .6 (.NET CLR 3.5.30729)", "언어" => "en-us,en;q=0.5"), "iphone" => 배열 ("user_agent" => "Mozilla/5.0 (iPhone; U ; Mac OS X와 ​​같은 CPU; en) AppleWebKit/420+(KHTML, like Gecko) Version/3.0 Mobile/1A537a Safari/419.3", "언어" => "en"), "프랑스어" => 배열("user_agent" => "Mozilla/4.0(호환; MSIE 7.0; Windows NT 5.1; GTB6; .NET CLR 2.0.50727)", "언어" => "fr,fr-FR;q=0.5")); foreach ($urls as $url) ( echo "URL: $url\n"; foreach ($browsers as $test_name => $browser) ( $ch = cur_init(); // URL 지정 cur_setopt($ch, CURLOPT_URL , $url); // 브라우저에 대한 헤더 지정curl_setopt($ch, CURLOPT_HTTPHEADER, array("User-Agent: ($browser["user_agent"])", "Accept-Language: ($browser["언어"] )" )); // 페이지의 내용은 필요하지 않습니다. cur_setopt($ch, CURLOPT_NOBODY, 1); // HTTP 헤더를 가져와야 합니다. cur_setopt($ch, CURLOPT_HEADER, 1); // 대신 결과를 반환합니다. 출력의 컬_setopt($ch, CURLOPT_RETURNTRANSFER, 1); $output = 컬_exec($ch); 컬_close($ch); // HTTP 리디렉션이 있었습니까? if (preg_match("!Location: (.*)!", $output, $matches)) ( echo " $test_name: $matches\n"로 리디렉션됩니다; ) else ( echo "$test_name: 리디렉션 없음\n"; ) ) echo "\n\n"; )

먼저 확인할 사이트의 URL 목록을 지정합니다. 보다 정확하게는 이러한 사이트의 주소가 필요합니다. 다음으로 이러한 각 URL을 테스트하기 위해 브라우저 설정을 정의해야 합니다. 그런 다음 얻은 모든 결과를 살펴보는 루프를 사용합니다.

이 예에서 cURL 설정을 지정하는 데 사용하는 트릭을 사용하면 페이지의 콘텐츠가 아니라 HTTP 헤더($output에 저장됨)만 가져올 수 있습니다. 다음으로, 간단한 정규식을 사용하여 "Location:" 문자열이 수신된 헤더에 존재하는지 여부를 확인할 수 있습니다.

이 코드를 실행하면 다음과 같은 결과가 나타납니다.

특정 URL에 대한 POST 요청 생성

GET 요청을 구성할 때 전송된 데이터는 "쿼리 문자열"을 통해 URL로 전달될 수 있습니다. 예를 들어, Google 검색을 수행하면 검색어가 새 URL의 주소 표시줄에 배치됩니다.

http://www.google.com/search?q=ruseller

이 요청을 시뮬레이션하기 위해 cURL을 사용할 필요는 없습니다. 게으름이 완전히 싫다면 “file_get_contents()” 함수를 사용하여 결과를 얻으세요.

그러나 문제는 일부 HTML 양식이 POST 요청을 보낸다는 것입니다. 이러한 양식의 데이터는 이전 사례와 달리 HTTP 요청 본문을 통해 전송됩니다. 예를 들어, 포럼에서 양식을 작성하고 검색 버튼을 클릭하면 POST 요청이 이루어질 가능성이 높습니다.

http://codeigniter.com/forums/do_search/

이러한 종류의 URL 요청을 시뮬레이션할 수 있는 PHP 스크립트를 작성할 수 있습니다. 먼저 POST 데이터를 받아들이고 표시하는 간단한 파일을 만들어 보겠습니다. post_output.php라고 부르자:

Print_r($_POST);

그런 다음 cURL 요청을 수행하는 PHP 스크립트를 만듭니다.

$url = "http://localhost/post_output.php"; $post_data = array ("foo" => "bar", "query" => "Nettuts", "action" => "제출"); $ch = 컬_초기화(); 컬_setopt($ch, CURLOPT_URL, $url); 컬_setopt($ch, CURLOPT_RETURNTRANSFER, 1); // POST 요청이 있음을 나타냅니다. cur_setopt($ch, CURLOPT_POST, 1); // 변수 추가curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data); $output = 컬_exec($ch); 컬_닫기($ch); 에코 $ 출력;

이 스크립트를 실행하면 다음과 같은 결과가 나타나야 합니다.

따라서 POST 요청은 post_output.php 스크립트로 전송되었으며, 이는 다시 cURL을 사용하여 얻은 내용인 슈퍼전역 $_POST 배열을 출력합니다.

파일 업로드

먼저 파일을 생성하여 upload_output.php 파일로 보내겠습니다.

Print_r($_FILES);

위의 기능을 수행하는 스크립트 코드는 다음과 같습니다.

$url = "http://localhost/upload_output.php"; $post_data = array ("foo" => "bar", // 업로드할 파일 "upload" => "@C:/wamp/www/test.zip"); $ch = 컬_초기화(); 컬_setopt($ch, CURLOPT_URL, $url); 컬_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 컬_setopt($ch, CURLOPT_POST, 1); 컬_setopt($ch, CURLOPT_POSTFIELDS, $post_data); $output = 컬_exec($ch); 컬_닫기($ch); 에코 $ 출력;

파일을 업로드하고 싶을 때 해야 할 일은 @ 기호 앞에 일반 게시물 변수로 파일을 전달하는 것뿐입니다. 작성된 스크립트를 실행하면 다음과 같은 결과를 얻게 됩니다.

다중 cURL

cURL의 가장 큰 장점 중 하나는 "다중" cURL 핸들러를 생성할 수 있다는 것입니다. 이를 통해 여러 URL에 대한 연결을 동시에 비동기적으로 열 수 있습니다.

cURL 요청의 클래식 버전에서는 스크립트 실행이 일시 중지되고 요청 URL 작업이 완료될 때까지 기다린 후 스크립트가 계속될 수 있습니다. 여러 URL과 상호 작용하려는 경우 클래식 버전에서는 한 번에 하나의 URL만 사용할 수 있으므로 상당한 시간 투자가 필요합니다. 그러나 특수 핸들러를 사용하면 이 상황을 해결할 수 있습니다.

php.net에서 가져온 예제 코드를 살펴보겠습니다.

// 여러 cURL 리소스 생성 $ch1 = cur_init(); $ch2 = 컬_init(); // URL 및 기타 매개변수를 지정합니다. curl_setopt($ch1, CURLOPT_URL, "http://lxr.php.net/"); 컬_setopt($ch1, CURLOPT_HEADER, 0); 컬_setopt($ch2, CURLOPT_URL, "http://www.php.net/"); 컬_setopt($ch2, CURLOPT_HEADER, 0); //다중 cURL 핸들러 생성 $mh = cur_multi_init(); //여러 핸들러 추가 컬_multi_add_handle($mh,$ch1); 컬_멀티_추가_핸들($mh,$ch2); $ 활성 = null; //실행 do ( $mrc ​​​​= cur_multi_exec($mh, $active); ) while ($mrc == CURLM_CALL_MULTI_PERFORM); while ($active && $mrc ​​​​== CURLM_OK) ( if (curl_multi_select($mh) != -1) ( do ( $mrc ​​​​= 컬_multi_exec($mh, $active); ) while ($mrc == CURLM_CALL_MULTI_PERFORM); ) ) //curl_multi_remove_handle($mh, $ch1) 닫기; curl_multi_remove_handle($mh, $ch2); 컬_다중_닫기($mh);

아이디어는 여러 cURL 처리기를 사용할 수 있다는 것입니다. 간단한 루프를 사용하면 아직 완료되지 않은 요청을 추적할 수 있습니다.

이 예에는 두 개의 주요 루프가 있습니다. 첫 번째 do-while 루프는curl_multi_exec()를 호출합니다. 이 기능은 차단할 수 없습니다. 가능한 한 빨리 실행되고 요청 상태를 반환합니다. 반환된 값이 'CURLM_CALL_MULTI_PERFORM' 상수인 한 이는 작업이 아직 완료되지 않았음을 의미합니다(예: http 헤더가 현재 URL로 전송되고 있음). 이것이 바로 우리가 다른 결과를 얻을 때까지 이 반환 값을 계속 확인하는 이유입니다.

다음 루프에서는 변수 $active = "true"인 동안 조건을 확인합니다. curl_multi_exec() 함수의 두 번째 매개변수입니다. 기존 변경 사항이 활성화되어 있는 한 이 변수의 값은 "true"입니다. 다음으로 우리는 cur_multi_select() 함수를 호출합니다. 응답이 수신될 때까지 하나 이상의 활성 연결이 있는 동안 실행이 "차단"됩니다. 이런 일이 발생하면 메인 루프로 돌아가 쿼리 실행을 계속합니다.

이제 이 지식을 많은 사람들에게 정말 유용할 예시에 적용해 보겠습니다.

WordPress에서 링크 확인하기

엄청난 수의 게시물과 메시지가 있고 각 게시물과 메시지에는 외부 인터넷 리소스에 대한 링크가 포함되어 있는 블로그를 상상해 보십시오. 이러한 링크 중 일부는 여러 가지 이유로 이미 작동하지 않을 수 있습니다. 페이지가 삭제되었거나 사이트가 전혀 작동하지 않을 수 있습니다.

우리는 모든 링크를 분석하고 로딩되지 않는 웹사이트와 404페이지를 찾아 상세한 보고서를 제공하는 스크립트를 생성할 예정입니다.

이것은 WordPress용 플러그인을 만드는 예가 아니라는 점을 바로 말씀드리겠습니다. 이것은 우리 테스트를 위한 절대적으로 좋은 테스트 장소입니다.

드디어 시작해 보겠습니다. 먼저 데이터베이스에서 모든 링크를 가져와야 합니다.

// 구성 $db_host = "localhost"; $db_user = "루트"; $db_pass = ""; $db_name = "워드프레스"; $excluded_domains = array("localhost", "www.mydomain.com"); $max_connections = 10; // 변수 초기화 $url_list = array(); $working_urls = 배열(); $dead_urls = 배열(); $not_found_urls = 배열(); $ 활성 = null; // MySQL에 연결 if (!mysql_connect($db_host, $db_user, $db_pass)) ( die("연결할 수 없습니다: " . mysql_error()); ) if (!mysql_select_db($db_name)) ( die("연결할 수 없습니다. not select db: " . mysql_error()); ) // 링크가 있는 모든 게시된 게시물을 선택합니다. $q = "SELECT post_content FROM wp_posts WHERE post_content LIKE "%href=%" AND post_status = "publish" AND post_type = "post " "; $r = mysql_query($q) 또는 die(mysql_error()); while ($d = mysql_fetch_assoc($r)) ( // 정규식을 사용하여 링크를 가져옵니다. if (preg_match_all("!href=\"(.*?)\"!", $d["post_content"], $ match) ) ( foreach ($matches as $url) ( $tmp = parse_url($url); if (in_array($tmp["host"], $excluded_domains)) ( continue; ) $url_list = $url; ) ) ) / / 중복 제거 $url_list = array_values(array_unique($url_list)); if (!$url_list) ( die("확인할 URL이 없습니다."); )

먼저 데이터베이스와의 상호 작용을 위한 구성 데이터를 생성한 다음 검사에 참여하지 않을 도메인 목록($excluded_domains)을 작성합니다. 또한 스크립트에서 사용할 최대 동시 연결 수를 나타내는 숫자($max_connections)를 정의합니다. 그런 다음 데이터베이스에 가입하고 링크가 포함된 게시물을 선택하여 배열($url_list)에 누적합니다.

다음 코드는 약간 복잡하므로 처음부터 끝까지 살펴보세요.

// 1. 다중 핸들러 $mh = cur_multi_init(); // 2. ($i = 0; $i에 대한 URL 세트를 추가합니다.< $max_connections; $i++) { add_url_to_multi_handle($mh, $url_list); } // 3. инициализация выполнения do { $mrc = curl_multi_exec($mh, $active); } while ($mrc == CURLM_CALL_MULTI_PERFORM); // 4. основной цикл while ($active && $mrc == CURLM_OK) { // 5. если всё прошло успешно if (curl_multi_select($mh) != -1) { // 6. делаем дело do { $mrc = curl_multi_exec($mh, $active); } while ($mrc == CURLM_CALL_MULTI_PERFORM); // 7. если есть инфа? if ($mhinfo = curl_multi_info_read($mh)) { // это значит, что запрос завершился // 8. извлекаем инфу $chinfo = curl_getinfo($mhinfo["handle"]); // 9. мёртвая ссылка? if (!$chinfo["http_code"]) { $dead_urls = $chinfo["url"]; // 10. 404? } else if ($chinfo["http_code"] == 404) { $not_found_urls = $chinfo["url"]; // 11. рабочая } else { $working_urls = $chinfo["url"]; } // 12. чистим за собой curl_multi_remove_handle($mh, $mhinfo["handle"]); // в случае зацикливания, закомментируйте данный вызов curl_close($mhinfo["handle"]); // 13. добавляем новый url и продолжаем работу if (add_url_to_multi_handle($mh, $url_list)) { do { $mrc = curl_multi_exec($mh, $active); } while ($mrc == CURLM_CALL_MULTI_PERFORM); } } } } // 14. завершение curl_multi_close($mh); echo "==Dead URLs==\n"; echo implode("\n",$dead_urls) . "\n\n"; echo "==404 URLs==\n"; echo implode("\n",$not_found_urls) . "\n\n"; echo "==Working URLs==\n"; echo implode("\n",$working_urls); function add_url_to_multi_handle($mh, $url_list) { static $index = 0; // если у нас есть ещё url, которые нужно достать if ($url_list[$index]) { // новый curl обработчик $ch = curl_init(); // указываем url curl_setopt($ch, CURLOPT_URL, $url_list[$index]); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); curl_setopt($ch, CURLOPT_NOBODY, 1); curl_multi_add_handle($mh, $ch); // переходим на следующий url $index++; return true; } else { // добавление новых URL завершено return false; } }

여기서는 모든 것을 자세히 설명하려고 노력할 것입니다. 목록의 숫자는 주석의 숫자와 일치합니다.

  1. 1. 다중 핸들러를 생성하십시오.
  2. 2. add_url_to_multi_handle() 함수는 잠시 후에 작성하겠습니다. 호출될 때마다 새 URL 처리가 시작됩니다. 처음에는 10개($max_connections)개의 URL을 추가합니다.
  3. 3. 시작하려면, cur_multi_exec() 함수를 실행해야 합니다. CURLM_CALL_MULTI_PERFORM을 반환하는 한 아직 해야 할 일이 있습니다. 이는 주로 연결을 생성하는 데 필요합니다.
  4. 4. 다음은 적어도 하나의 활성 연결이 있는 한 실행되는 메인 루프입니다.
  5. 5. URL 검색이 완료될 때까지 컬_멀티_선택()이 정지됩니다.
  6. 6. 다시 한번, 일부 작업, 즉 반환 응답 데이터를 가져오기 위해 cURL을 가져와야 합니다.
  7. 7. 여기에서 정보를 확인합니다. 요청을 실행한 결과 배열이 반환됩니다.
  8. 8. 반환된 배열에는 cURL 처리기가 포함되어 있습니다. 이를 사용하여 별도의 cURL 요청에 대한 정보를 선택합니다.
  9. 9. 링크가 끊어졌거나 스크립트 시간이 초과된 경우 http 코드를 검색해서는 안 됩니다.
  10. 10. 링크가 404 페이지를 반환한 경우 http 코드에는 값 404가 포함됩니다.
  11. 11. 그렇지 않으면 우리 앞에 작동 링크가 있습니다. (오류 코드 500 등에 대한 추가 검사를 추가할 수 있습니다...)
  12. 12. 다음으로 cURL 핸들러는 더 이상 필요하지 않으므로 제거합니다.
  13. 13. 이제 다른 URL을 추가하고 이전에 이야기한 모든 내용을 실행할 수 있습니다.
  14. 14. 이 단계에서 스크립트는 작업을 완료합니다. 필요하지 않은 모든 것을 제거하고 보고서를 생성할 수 있습니다.
  15. 15. 마지막으로 핸들러에 URL을 추가하는 함수를 작성하겠습니다. 이 함수가 호출될 때마다 정적 변수 $index가 증가합니다.

나는 내 블로그에서 이 스크립트를 사용했고(테스트를 위해 일부러 깨진 링크를 추가했습니다) 다음과 같은 결과를 얻었습니다.

내 경우에는 스크립트가 40개의 URL을 크롤링하는 데 2초도 채 걸리지 않았습니다. 더 많은 URL로 작업할 때 성능이 크게 향상됩니다. 동시에 10개의 연결을 열면 스크립트가 10배 더 빠르게 실행될 수 있습니다.

다른 유용한 cURL 옵션에 대한 몇 마디

HTTP 인증

URL에 HTTP 인증이 있으면 다음 스크립트를 쉽게 사용할 수 있습니다.

$url = "http://www.somesite.com/members/"; $ch = 컬_초기화(); 컬_setopt($ch, CURLOPT_URL, $url); 컬_setopt($ch, CURLOPT_RETURNTRANSFER, 1); // 사용자 이름과 비밀번호를 지정합니다. cur_setopt($ch, CURLOPT_USERPWD, "myusername:mypassword"); // 리디렉션이 허용되는 경우 cur_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); // 그런 다음 cURL에 데이터를 저장합니다. cur_setopt($ch, CURLOPT_UNRESTRICTED_AUTH, 1); $output = 컬_exec($ch); 컬_닫기($ch);

FTP 업로드

PHP에는 FTP 작업을 위한 라이브러리도 있지만 여기서 cURL 도구를 사용하는 데 방해가 되는 것은 없습니다.

// 파일 열기 $file = fopen("/path/to/file", "r"); // URL에는 다음 내용이 포함되어야 합니다. $url = "ftp://username: [이메일 보호됨]:21/path/to/new/file"; $ch = 컬_init(); 컬_setopt($ch, CURLOPT_URL, $url); 컬_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 컬_setopt($ch, CURLOPT_UPLOAD, 1); curl_setopt($ch, CURLOPT_INFILE, $fp);curl_setopt($ch, CURLOPT_INFILESIZE,filesize("/path/to/file")); //ASCII 모드를 지정합니다.curl_setopt($ch, CURLOPT_FTPASCII, 1);$output = 컬_exec($ch), 컬_닫기($ch);

프록시 사용

프록시를 통해 URL 요청을 수행할 수 있습니다.

$ch = 컬_초기화(); 컬_setopt($ch, CURLOPT_URL,"http://www.example.com"); 컬_setopt($ch, CURLOPT_RETURNTRANSFER, 1); // 주소 지정 cur_setopt($ch, CURLOPT_PROXY, "11.11.11.11:8080"); // 사용자 이름과 비밀번호를 제공해야 하는 경우 cur_setopt($ch, CURLOPT_PROXYUSERPWD,"user:pass"); $output = 컬_exec($ch); 컬_닫기($ch);

콜백 함수

cURL 요청이 완료되기 전에도 트리거될 함수를 지정할 수도 있습니다. 예를 들어 응답 콘텐츠가 로드되는 동안 완전히 로드될 때까지 기다리지 않고 데이터 사용을 시작할 수 있습니다.

$ch = 컬_초기화(); cur_setopt($ch, CURLOPT_URL,"http://net.tutsplus.com"); 컬_setopt($ch, CURLOPT_WRITEFUNCTION,"진행_기능"); 컬_exec($ch); 컬_닫기($ch); 함수 Progress_function($ch,$str) ( echo $str; return strlen($str); )

이와 같은 함수는 요구 사항인 문자열 길이를 반환해야 합니다.

결론

오늘 우리는 cURL 라이브러리를 자신의 이기적인 목적으로 사용하는 방법을 배웠습니다. 이 기사를 즐겼기를 바랍니다.

감사합니다! 좋은 하루 보내세요!

공유하다