사용자 프로필, 업무 정보, 상세 프로필 데이터를 병렬로 수집하고 이를 처리하는 과정을 구현한 예제
1. 주요 상수 정의
API 요청에 필요한 URL들을 상수로 정의합니다.
const (
LoginURL = "https://auth-api.office.hiworks.com/office-web/login"
ProfileURL = "https://cache-api.office.hiworks.com/me"
WorksURL = "https://hr-timecheck-api.office.hiworks.com/v4/web/user-work-info"
ProfileDetailURL = "https://office.hiworks.com/moasoftware.onhiworks.com/insa/org_ajax/"
)
2. 에러 핸들링 함수
에러 발생 시 프로그램을 종료합니다.
func handleError(err error) {
if err != nil {
log.Fatal(err)
}
}
3. 로그인 요청 함수
ID와 비밀번호를 사용해 로그인 요청을 보내고, 응답 쿠키를 반환합니다.
func login() (map[string]string, error) {
payload := `{
"id": "___@moasoftware.onhiworks.com",
"password": "______",
"ip_security_level": "1"
}`
_, res, err := network.PostJSONRequest(LoginURL, payload, nil)
if err != nil {
return nil, err
}
defer res.Body.Close()
// 응답 쿠키 파싱
cookieMap := make(map[string]string)
for _, cookie := range res.Cookies() {
cookieMap[cookie.Name] = cookie.Value
}
return cookieMap, nil
}
4. 프로필 정보 조회 함수
ProfileURL로 요청을 보내 사용자 프로필 데이터를 가져옵니다.
func fetchProfile(cookies map[string]string, wg *sync.WaitGroup, profileChan chan<- *types.MyProfileResponse, errChan chan<- error) {
defer wg.Done()
body, err := network.GetRequest(ProfileURL, cookies)
if err != nil {
errChan <- err
return
}
var profile_response types.MyProfileResponse
if err = json.Unmarshal(body, &profile_response); err != nil {
errChan <- err
return
}
profileChan <- &profile_response
}
5. 업무 정보 조회 함수
WorksURL로 요청을 보내 사용자의 출퇴근 기록을 가져옵니다.
func fetchWorksInfo(cookies map[string]string, wg *sync.WaitGroup, worksChan chan<- *types.MyWorksInfoResponse, errChan chan<- error) {
defer wg.Done()
body, err := network.GetRequest(WorksURL, cookies)
if err != nil {
errChan <- err
return
}
var works_response types.MyWorksInfoResponse
if err = json.Unmarshal(body, &works_response); err != nil {
errChan <- err
return
}
worksChan <- &works_response
}
메인 함수
로그인 후 데이터를 병렬로 수집하며, 필요한 데이터가 모두 수집되면 프로그램을 종료합니다.
func main() {
start := time.Now()
cookies, err := login()
handleError(err)
if cookies["PHPSESSID"] == "" {
log.Fatal("로그인 실패")
} else {
log.Println("로그인 성공")
}
var wg sync.WaitGroup
profileChan := make(chan *types.MyProfileResponse)
worksChan := make(chan *types.MyWorksInfoResponse)
detailChan := make(chan *types.ProfileDetail)
errChan := make(chan error, 3)
wg.Add(2)
go fetchProfile(cookies, &wg, profileChan, errChan)
go fetchWorksInfo(cookies, &wg, worksChan, errChan)
go func() {
wg.Wait()
close(profileChan)
close(worksChan)
close(detailChan)
close(errChan)
}()
var profile *types.MyProfileResponse
var works *types.MyWorksInfoResponse
var profileDetail *types.ProfileDetail
for {
select {
case p := <-profileChan:
profile = p
wg.Add(1)
go fetchProfileDetail(cookies, "55734", &wg, detailChan, errChan)
case w := <-worksChan:
works = w
case pd := <-detailChan:
profileDetail = pd
case err := <-errChan:
handleError(err)
return
}
if profile != nil && works != nil && profileDetail != nil {
break
}
}
em, err := parse.ParseEmployInfo(profileDetail.Result)
if err != nil {
handleError(err)
}
fmt.Println(em.Name, em.Department, em.Position, em.PhoneNumber, em.Email, em.EmployeeID, em.HireDate, em.BirthDate)
end := time.Now()
fmt.Println("실행 시간:", end.Sub(start))
}
병렬 처리 기법인 고루틴, 데이터 전달과 동기화를 위한 채널, 작업 완료 대기를 위한 WaitGroup 및 동시성 작업 구조를 단순화하는 익명 함수를 활용하여 설계
- 프로그램에서 fetchProfile, fetchWorksInfo, fetchProfileDetail 등의 함수가 각각 별도의 고루틴으로 실행
- 프로그램은 각 작업의 결과를 전달하거나 오류를 처리하기 위해 여러 개의 채널을 사용
- 프로그램은 비동기 작업을 병렬로 실행하고, 모든 작업이 완료될 때까지 대기하도록 WaitGroup을 활용
- 프로그램에서 익명 함수는 wg.Wait() 이후 모든 채널을 닫는 작업을 수행합니다. 이는 코드의 간결함을 높이고 반복적인 작업 줄임
- 각 채널에서 데이터를 수신할 때마다 특정 작업을 실행하며, 모든 데이터가 수집되면 루프를 종료
주요 흐름
- 로그인: Hiworks API를 통해 인증 후 쿠키를 수신.
- 병렬 작업 시작:
- fetchProfile과 fetchWorksInfo를 고루틴으로 실행.
- 채널로 데이터 수집:
- select 구문으로 채널에서 데이터 수신.
- 추가 작업 병렬 실행:
- 프로필 데이터 수신 후 fetchProfileDetail 실행.
- 작업 완료 대기:
- WaitGroup을 활용해 모든 고루틴 작업 완료 대기.
- 결과 처리:
- 수집한 데이터를 JSON으로 파싱하고 필요한 정보를 출력.
'Go' 카테고리의 다른 글
[Go][gin] 리액트 정적 파일 서빙 (0) | 2025.02.15 |
---|---|
[Go] HTTP Client 크롤링 최적화 (0) | 2025.01.22 |
Go언어 재설치하기 ( Ubuntu ) (0) | 2025.01.01 |