Skip to content

Commit 087f44b

Browse files
committed
refactor(go): API 클라이언트 DI 패턴 적용
매 API 호출마다 새로운 HTTP 클라이언트를 생성하던 구조를 개선하여 클라이언트를 재사용할 수 있도록 DI 패턴 적용 - APIClient 인터페이스 정의로 테스트 시 mock 주입 가능 - 각 Scraper 생성자가 client를 주입받도록 변경 fix #258
1 parent 1a5ed80 commit 087f44b

14 files changed

Lines changed: 279 additions & 45 deletions

File tree

src/go/apps/auction-item-stat-scraper/main.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"github.com/KubrickCode/loa-work/src/go/apps/auction-item-stat-scraper/converter"
99
"github.com/KubrickCode/loa-work/src/go/apps/auction-item-stat-scraper/scraper"
1010
"github.com/KubrickCode/loa-work/src/go/libs/env"
11+
"github.com/KubrickCode/loa-work/src/go/libs/loaApi/request"
1112
"github.com/KubrickCode/loa-work/src/go/libs/loadb"
1213
"github.com/KubrickCode/loa-work/src/go/libs/monitoring"
1314
"github.com/KubrickCode/loa-work/src/go/libs/schedule"
@@ -27,7 +28,8 @@ func main() {
2728
log.Fatal(err)
2829
}
2930

30-
scraper := scraper.NewScraper(db)
31+
client := request.NewClient()
32+
scraper := scraper.NewScraper(client, db)
3133
converter := converter.NewConverter(db)
3234

3335
combinedTask := func() error {

src/go/apps/auction-item-stat-scraper/scraper/scraper.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,17 @@ import (
1414
)
1515

1616
type Scraper struct {
17+
client request.APIClient
1718
db loadb.DB
1819
rateLimiter *rate.Limiter
1920
}
2021

21-
func NewScraper(db loadb.DB) *Scraper {
22-
return &Scraper{db: db, rateLimiter: rate.NewLimiter(rate.Every(time.Second), 1)}
22+
func NewScraper(client request.APIClient, db loadb.DB) *Scraper {
23+
return &Scraper{
24+
client: client,
25+
db: db,
26+
rateLimiter: rate.NewLimiter(rate.Every(time.Second), 1),
27+
}
2328
}
2429

2530
func (s *Scraper) Start() error {
@@ -67,7 +72,7 @@ func (s *Scraper) getItemStatsToCreate(category *models.AuctionItemCategory, ite
6772
return nil, fmt.Errorf("rate limiter error: %w", err)
6873
}
6974

70-
auctionItemListResp, err := request.GetAuctionItemList(&loaApi.GetAuctionItemListParams{
75+
auctionItemListResp, err := s.client.GetAuctionItemList(&loaApi.GetAuctionItemListParams{
7176
CategoryCode: category.Code,
7277
ItemName: item.Name,
7378
PageNo: 1,

src/go/apps/market-item-category-scraper/main.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"log"
55

66
"github.com/KubrickCode/loa-work/src/go/apps/market-item-category-scraper/scraper"
7+
"github.com/KubrickCode/loa-work/src/go/libs/loaApi/request"
78
"github.com/KubrickCode/loa-work/src/go/libs/loadb"
89
)
910

@@ -13,7 +14,8 @@ func main() {
1314
log.Fatal(err)
1415
}
1516

16-
scraper := scraper.NewScraper(db)
17+
client := request.NewClient()
18+
scraper := scraper.NewScraper(client, db)
1719

1820
err = scraper.Start()
1921
if err != nil {

src/go/apps/market-item-category-scraper/scraper/scraper.go

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package scraper
22

33
import (
4+
"errors"
45
"fmt"
56
"log"
67

@@ -9,12 +10,18 @@ import (
910
"github.com/KubrickCode/loa-work/src/go/libs/loadb/models"
1011
)
1112

13+
var ErrNoMarketItemCategories = errors.New("no market item categories found")
14+
1215
type Scraper struct {
13-
db loadb.DB
16+
client request.APIClient
17+
db loadb.DB
1418
}
1519

16-
func NewScraper(db loadb.DB) *Scraper {
17-
return &Scraper{db: db}
20+
func NewScraper(client request.APIClient, db loadb.DB) *Scraper {
21+
return &Scraper{
22+
client: client,
23+
db: db,
24+
}
1825
}
1926

2027
func (s *Scraper) Start() error {
@@ -34,15 +41,15 @@ func (s *Scraper) Start() error {
3441
}
3542

3643
func (s *Scraper) getCategories() ([]*models.MarketItemCategory, error) {
37-
resp, err := request.GetCategoryList()
44+
resp, err := s.client.GetCategoryList()
3845
if err != nil {
3946
return nil, err
4047
}
4148

4249
categories := GetFlattenCategories(resp.Categories)
4350

4451
if len(categories) == 0 {
45-
return nil, fmt.Errorf("no market item categories found")
52+
return nil, ErrNoMarketItemCategories
4653
}
4754

4855
return categories, nil
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
package scraper
2+
3+
import (
4+
"errors"
5+
"testing"
6+
7+
"github.com/KubrickCode/loa-work/src/go/libs/loaApi"
8+
)
9+
10+
type mockAPIClient struct {
11+
getAuctionItemListFunc func(params *loaApi.GetAuctionItemListParams) (*loaApi.GetAuctionItemListResponse, error)
12+
getCategoryListFunc func() (*loaApi.GetCategoryListResponse, error)
13+
getMarketItemFunc func(params *loaApi.GetMarketItemParams) (*loaApi.GetMarketItemResponse, error)
14+
getMarketItemListFunc func(params *loaApi.GetMarketItemListParams) (*loaApi.GetMarketItemListResponse, error)
15+
}
16+
17+
func (m *mockAPIClient) GetAuctionItemList(params *loaApi.GetAuctionItemListParams) (*loaApi.GetAuctionItemListResponse, error) {
18+
if m.getAuctionItemListFunc != nil {
19+
return m.getAuctionItemListFunc(params)
20+
}
21+
return nil, errors.New("not implemented")
22+
}
23+
24+
func (m *mockAPIClient) GetCategoryList() (*loaApi.GetCategoryListResponse, error) {
25+
if m.getCategoryListFunc != nil {
26+
return m.getCategoryListFunc()
27+
}
28+
return nil, errors.New("not implemented")
29+
}
30+
31+
func (m *mockAPIClient) GetMarketItem(params *loaApi.GetMarketItemParams) (*loaApi.GetMarketItemResponse, error) {
32+
if m.getMarketItemFunc != nil {
33+
return m.getMarketItemFunc(params)
34+
}
35+
return nil, errors.New("not implemented")
36+
}
37+
38+
func (m *mockAPIClient) GetMarketItemList(params *loaApi.GetMarketItemListParams) (*loaApi.GetMarketItemListResponse, error) {
39+
if m.getMarketItemListFunc != nil {
40+
return m.getMarketItemListFunc(params)
41+
}
42+
return nil, errors.New("not implemented")
43+
}
44+
45+
func TestGetCategories_Success(t *testing.T) {
46+
mockClient := &mockAPIClient{
47+
getCategoryListFunc: func() (*loaApi.GetCategoryListResponse, error) {
48+
return &loaApi.GetCategoryListResponse{
49+
Categories: []loaApi.Category{
50+
{
51+
Code: 10000,
52+
CodeName: "Test Category 1",
53+
Subs: []loaApi.SubCategory{
54+
{Code: 10001, CodeName: "Test Sub Category 1"},
55+
},
56+
},
57+
{
58+
Code: 20000,
59+
CodeName: "Test Category 2",
60+
Subs: []loaApi.SubCategory{},
61+
},
62+
},
63+
}, nil
64+
},
65+
}
66+
67+
scraper := &Scraper{
68+
client: mockClient,
69+
db: nil,
70+
}
71+
72+
categories, err := scraper.getCategories()
73+
if err != nil {
74+
t.Fatalf("Expected no error, got %v", err)
75+
}
76+
77+
if len(categories) != 3 {
78+
t.Errorf("Expected 3 categories (2 parent + 1 sub), got %d", len(categories))
79+
}
80+
81+
expectedCodes := []int{10000, 10001, 20000}
82+
for i, category := range categories {
83+
if category.Code != expectedCodes[i] {
84+
t.Errorf("Expected category code %d, got %d", expectedCodes[i], category.Code)
85+
}
86+
}
87+
}
88+
89+
func TestGetCategories_APIError(t *testing.T) {
90+
expectedErr := errors.New("API connection failed")
91+
mockClient := &mockAPIClient{
92+
getCategoryListFunc: func() (*loaApi.GetCategoryListResponse, error) {
93+
return nil, expectedErr
94+
},
95+
}
96+
97+
scraper := &Scraper{
98+
client: mockClient,
99+
db: nil,
100+
}
101+
102+
_, err := scraper.getCategories()
103+
if err == nil {
104+
t.Fatal("Expected error, got nil")
105+
}
106+
107+
if err != expectedErr {
108+
t.Errorf("Expected error %v, got %v", expectedErr, err)
109+
}
110+
}
111+
112+
func TestGetCategories_EmptyResponse(t *testing.T) {
113+
mockClient := &mockAPIClient{
114+
getCategoryListFunc: func() (*loaApi.GetCategoryListResponse, error) {
115+
return &loaApi.GetCategoryListResponse{
116+
Categories: []loaApi.Category{},
117+
}, nil
118+
},
119+
}
120+
121+
scraper := &Scraper{
122+
client: mockClient,
123+
db: nil,
124+
}
125+
126+
_, err := scraper.getCategories()
127+
if err == nil {
128+
t.Fatal("Expected error for empty categories, got nil")
129+
}
130+
131+
if !errors.Is(err, ErrNoMarketItemCategories) {
132+
t.Errorf("Expected ErrNoMarketItemCategories, got %v", err)
133+
}
134+
}
135+
136+
func TestGetFlattenCategories(t *testing.T) {
137+
categories := []loaApi.Category{
138+
{
139+
Code: 10000,
140+
CodeName: "Parent 1",
141+
Subs: []loaApi.SubCategory{
142+
{Code: 10001, CodeName: "Child 1-1"},
143+
{Code: 10002, CodeName: "Child 1-2"},
144+
},
145+
},
146+
{
147+
Code: 20000,
148+
CodeName: "Parent 2",
149+
Subs: []loaApi.SubCategory{
150+
{Code: 20001, CodeName: "Child 2-1"},
151+
},
152+
},
153+
}
154+
155+
flattened := GetFlattenCategories(categories)
156+
157+
if len(flattened) != 5 {
158+
t.Errorf("Expected 5 flattened categories, got %d", len(flattened))
159+
}
160+
161+
expectedCodes := []int{10000, 10001, 10002, 20000, 20001}
162+
for i, category := range flattened {
163+
if category.Code != expectedCodes[i] {
164+
t.Errorf("At index %d: expected code %d, got %d", i, expectedCodes[i], category.Code)
165+
}
166+
}
167+
168+
for _, category := range flattened {
169+
if category.ID != 0 {
170+
t.Errorf("Expected ID to be 0 (new record), got %d", category.ID)
171+
}
172+
}
173+
}
174+
175+
func TestGetFlattenCategories_EmptySubCategories(t *testing.T) {
176+
categories := []loaApi.Category{
177+
{
178+
Code: 10000,
179+
CodeName: "Category without subs",
180+
Subs: []loaApi.SubCategory{},
181+
},
182+
}
183+
184+
flattened := GetFlattenCategories(categories)
185+
186+
if len(flattened) != 1 {
187+
t.Errorf("Expected 1 category, got %d", len(flattened))
188+
}
189+
190+
if flattened[0].Code != 10000 {
191+
t.Errorf("Expected code 10000, got %d", flattened[0].Code)
192+
}
193+
194+
if flattened[0].Name != "Category without subs" {
195+
t.Errorf("Expected name 'Category without subs', got %s", flattened[0].Name)
196+
}
197+
}

src/go/apps/market-item-scraper/main.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"log"
55

66
"github.com/KubrickCode/loa-work/src/go/apps/market-item-scraper/scraper"
7+
"github.com/KubrickCode/loa-work/src/go/libs/loaApi/request"
78
"github.com/KubrickCode/loa-work/src/go/libs/loadb"
89
)
910

@@ -13,7 +14,8 @@ func main() {
1314
log.Fatal(err)
1415
}
1516

16-
scraper := scraper.NewScraper(db)
17+
client := request.NewClient()
18+
scraper := scraper.NewScraper(client, db)
1719

1820
err = scraper.Start()
1921
if err != nil {

src/go/apps/market-item-scraper/scraper/scraper.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,15 @@ import (
1111
)
1212

1313
type Scraper struct {
14-
db loadb.DB
14+
client request.APIClient
15+
db loadb.DB
1516
}
1617

17-
func NewScraper(db loadb.DB) *Scraper {
18-
return &Scraper{db: db}
18+
func NewScraper(client request.APIClient, db loadb.DB) *Scraper {
19+
return &Scraper{
20+
client: client,
21+
db: db,
22+
}
1923
}
2024

2125
func (s *Scraper) Start() error {
@@ -58,7 +62,7 @@ func (s *Scraper) getItemsToSave(categories []*models.MarketItemCategory) ([]*mo
5862
pageNo := 1
5963

6064
for {
61-
resp, err := request.GetMarketItemList(&loaApi.GetMarketItemListParams{
65+
resp, err := s.client.GetMarketItemList(&loaApi.GetMarketItemListParams{
6266
CategoryCode: category.Code,
6367
PageNo: pageNo,
6468
})

src/go/apps/market-item-stat-scraper/main.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"github.com/KubrickCode/loa-work/src/go/apps/market-item-stat-scraper/converter"
99
"github.com/KubrickCode/loa-work/src/go/apps/market-item-stat-scraper/scraper"
1010
"github.com/KubrickCode/loa-work/src/go/libs/env"
11+
"github.com/KubrickCode/loa-work/src/go/libs/loaApi/request"
1112
"github.com/KubrickCode/loa-work/src/go/libs/loadb"
1213
"github.com/KubrickCode/loa-work/src/go/libs/monitoring"
1314
"github.com/KubrickCode/loa-work/src/go/libs/schedule"
@@ -27,7 +28,8 @@ func main() {
2728
log.Fatal(err)
2829
}
2930

30-
scraper := scraper.NewScraper(db)
31+
client := request.NewClient()
32+
scraper := scraper.NewScraper(client, db)
3133
converter := converter.NewConverter(db)
3234

3335
combinedTask := func() error {

0 commit comments

Comments
 (0)