[GO]URL Checker

포동동·2022년 7월 24일
0

GOLANG

목록 보기
3/4

URL Checker 만들기

url 목록을 전달하고 접속이 가능한지 확인해주는 프로그램을 만들어보자.

(호옥시나 오해할까봐 그러는데 이건 니꼴라스의 강의에 기초해서 만든 것이다)

두 번 보세요 니꼴라스 샘👍

온갖 url들을 집어넣고 접속을 시켜서 제대로 접속되는지 확인하는 프로그램을 만들것이고, 그리고 중요한 것이✨goroutine✨이나 💥channel💥 기능도 활용해 볼 생각이다.

이번엔 그냥 main.go에서 다 작성할 예정이다(따로 package 만들어서 도전해봤지만 runtime 에러가 자꾸 나서 포기😧)


STEP1. 그냥 URL Checker 만들기

우선 생각해볼 것은,

  • url 리스트를 for문으로 훑을 것
  • 그 url들을 들어가서 확인시킬 것
  • 그 결과들을 "Connected"나 "Failed"로 확인할 것
  • 결과적으로 {url이름 : status}의 형태로 받을 것

먼저 hitURL 이라는 function을 먼저 작성해준다.


var errCant = errors.New("Cant Connect")

func hitURL(url string) error {
	fmt.Println("Checking :", url)
	resp, err := http.Get(url)
	if err != nil || resp.StatusCode >= 400 {
		return errCant
	}
	return nil
}

우선 http에 들어가는 메서드는 http.Get이다. 결과값은 responseerr이기 때문에 둘 다 확인해서 err가 있거나 statuscode가 400 이상(접속 자체에 문제)인 경우에는 에러 메세지를 띄우도록 한다.


그 다음, main.go에 들어가 main function에 아래와 같이 코드를 작성한다.


package main

import (
	"errors"
	"fmt"
	"net/http"
)

func main() {
	var results = make(map[string]string)

	urls := []string{
		"https://www.naver.com/",
		"https://www.youtube.com/",
		"https://www.github.com/",
		"https://your-naduri.herokuapp.com/",
		"https://www.podo.com/",
	}
	for _, url := range urls {
		result := "Connected"
		err := hitURL(url)
		if err != nil {
			result = "Failed"
		}
		results[url] = result
	}
	for url, result := range results {
		fmt.Println(url, result)
	}
}

우선 {url이름 : status}의 형태의 결과를 받을 빈 map을 하나 만들어주는데 초기화를 해주지 않으면 값을 넣을 수 없으니 항상 주의❗하자. 여기서는 make()를 사용하였다.

그 다음으로, urls라는 url 리스트를 돌면서 확인을 해줄 첫 번째 for문을 작성한다.

위의 코드에서는 아래의 부분에 해당한다.

for _, url := range urls {
		result := "Connected"
		err := hitURL(url)
		if err != nil {
			result = "Failed"
		}
		results[url] = result

여기서는 result의 기본값을 "Connected"로 하였고, 만약 err가 있다면 result를 "Failed"로 바꿔주도록 하였다.

이 상태로 실행시키면 map이 띄워지지 않고 한 번에 다 나와서 두 번째 for문을 열어서 따로따로 result들을 보기 좋게 한 줄씩 프린트 하였다.

아래는 실행 결과이다.

실제로 없는 "www.podo.com"은 Failed로 나온 것을 알 수 있다 ✅


다만, 이렇게 하면 굉장히 느리다.
왜냐, 기본적으로 순차적으로 url들을 돌기 때문에
내가 만들고 heroku로 배포한 "your-naduri.herokuapp.com"같은 경우에는
접속에 시간이 좀 걸리는데 그러면 정체되기 때문에
파일을 다 실행시키는데 약 5~10초가 걸린다(Go를 쓰는 의미가 없다).

따라서, ✨goroutine✨이나 💥channel💥이라는 기능을 도입해서 병렬적으로 thread를 진행시켜보자.



STEP2. 빠른 URL Checker 만들기

그 전에, 간단히 ✨goroutine✨과 💥channel💥에 대해서 설명해보겠다(테디베어....🐻)


goroutine

Go를 매력적으로 하게 만드는 장본인(Concurrency 만만세😍). 다른 프로그램 언어들이 순차적으로 진행되는 (객체지향도 결국 객체 안에서는 순서대로 진행된다) 일들을 병렬적으로 한 번에 쫘아아아악(순서 무관) 해준다. 게다가 사용법도 완전 간단. main function 안에서 실행시킬 function 앞에 go라고만 써주면 😎

예제를 보자. 코드 참조

package main

import (
  "fmt"
  "time"
)

func hello(name string) {
  for i := 0; i < 3; i++ {
    fmt.Println(name, " - ", i)
  }
}

func main() {
  go hello("Ricky")
  go hello("Tom")
  go hello("Alice")
  
  time.Sleep(time.Second * 5)
}

위의 코드를 보면 hello라는 function 안에는 이름을 받아 0,1,2이라는 숫자를 붙여 출력하는 구조가 짜여져있다.

그리고 아래 main function 안에는 각각 hello라는 함수를 호출에 다른 이름들을 넣어주었다. 출력 결과는 아래와 같다.

첫 번째 실행한 결과와 두 번째 실행한 결과가 다른 것을 볼 수 있는데, 그게 바로 GOT✨동시성(Concurrency)✨GOT이다. 한 번에 병렬적으로 thread를 처리한다.

다만 주의할 것이, goroutinemain function이 실행될 때만 발동하기 때문에, goroutine을 사용할 함수 외에 하나 더 함수를 붙여주던가, 아니면 timeSleep(함수를 몇 초 동안 실행시켜둘지)을 써주어야 한다. 즉, 아래와 같이 써야한다.

func main() {
  go hello("Ricky")
  go hello("Tom")
  go hello("Alice")
  
  time.Sleep(time.Second * 5)
}

// 위와 같이 쓰던가, 아님 아래와 같이 써야한다.

func main() {
  go hello("Ricky")
  go hello("Tom")
  hello("Alice")
}

(그 외에도 익명함수+Add+Done+Wait 등 많은 기능이 있으니 구글링 ㄱㄱ)


💥channel💥

goroutine으로 반환받은 결과를 main function에게 전달하거나 다른 goroutine에게 전달하기위한 🗜파이프🗜 같은 거라고 생각하면 된다.

사용법은 이것도 엄청 간단하다. function안에서 채널명 (통상 c)<- 결과값으로 받아서 main function에서 <-채널명(통상 c)으로 주면 된다.

예제를 보자.

package main

import (
	"fmt"
	"time"
)

func hello(str string, c chan string) {
	time.Sleep(time.Second * 3)
	c <- str + ", hi!"
}

func main() {
	c := make(chan string)
	people := [3]string{"Ricky", "Tom", "Alice"}
	for _, person := range people {
		go hello(person, c)
	}
	for i := 0; i < len(people); i++ {
		fmt.Println(<-c)
	}
}

hello function 안에서 str + ", hi!"를 결과값으로 c에 저장해주고(대신 이때 function 정의할 때 채널 받을 거라고 명시해줘야 한다)

그 다음, main function에 가서 for문 두 개 돌리면서 결과값을 출력시키면 된다. 여기서 특이한 점이 time.Sleep이 없다는 점이다. 왜냐, 어차피 cpeople의 길이대로 다 받아야하기 때문에 그 때 goroutine이 그 때 끝나서 그렇다.

결과를 보자.

똑같이 실행한 두 개의 결과이다. 앞선 코드와 마찬가지로 출력 결과의 순서는 의미없다. 그냥 병렬적으로 처리되서 거의 동시에 실행된 것이다.


자, 다시 본 URL Checker로 돌아가자. 아래와 같이 작성하자.

package main

import (
	"fmt"
	"net/http"
)

type result struct {
	url    string
	status string
}

func main() {
	c := make(chan result)

	urls := []string{
		"https://www.naver.com/",
		"https://www.youtube.com/",
		"https://www.github.com/",
		"https://your-naduri.herokuapp.com/",
		"https://www.podo.com/",
	}
	for _, url := range urls {
		go hitURL(url, c)
	}
	for i := 0; i < len(urls); i++ {
		fmt.Println(<-c)
	}
}

func hitURL(url string, c chan result) {
	fmt.Println("Checking :", url)
	resp, err := http.Get(url)
	status := "Connected"

	if err != nil || resp.StatusCode >= 400 {
		status = "Failed"
	}
	c <- result{url: url, status: status}
}

우선, result를 struct로 담아주게 빈 struct를 만들어주고, main function에 채널을 추가해준다.

그 다음 hitURL function부터 보면, struct에 들어갈 결과값들(여기서는 result{url: url, status: status})를 c 채널에 넣어준다.

그러고 main function에 가서 go hitURL(url, c)로 받아주고 그 아래 for문으로 fmt.Println(<-c)로 싸악 다 출력해준다.

이렇게 하면 아래와 같은 결과가 출력된다.

goroutine을 적용시켰기때문에 순서는 상관없고, 중요한 건 이 모든 것이 1초도 안 되서 실행되었다는 것이다.

goroutine을 사용하지 않았을 때는 5~10초정도 걸렸는데 이걸 1초로 줄였다 ✅



마무리🙆‍

Go의 진짜 매력을 알아볼 수 있는 재밌는 실습이었던 것 같다. 이 기능을 배우니 Go에 대한 흥미가 더 많아졌다🤲💯

딥러닝 프로젝트를 얼른 대충 마무리 짓고 더 공부하고 싶다.

profile
완료주의

0개의 댓글