Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/#152 인스타그램 이미지 조회 문제 수정 #155

Merged
merged 17 commits into from
Nov 18, 2024

Conversation

ShapeKim98
Copy link
Contributor

#️⃣연관된 이슈

#152

📝작업 내용

이번 PR에서 작업한 내용을 간략히 설명해주세요(이미지 첨부 가능)

  • SwiftSoupClient 로직 변경
  • FeatureContentCard모듈, ConentCardFeature, ContentCardView 추가
  • PokitLinkCard 컴포넌트 추가 로직 작성

스크린샷 (선택)

💬리뷰 요구사항(선택)

리뷰어가 특별히 봐주었으면 하는 부분이 있다면 작성해주세요

SwiftSoupClient 로직을 변경하였습니다.

  • 먼저 인스타그램과 같이 이미지 조회에 문제가 있는 링크의 이미지만 따로 파싱하기 위해 og 이미지와 제목을 한번에 파싱하는 기존 로직과 다르게 이미지, 제목 파싱을 따로 작성하였습니다.
@DependencyClient
public struct SwiftSoupClient {
    public var parseOGTitle: @Sendable (
        _ url: URL
    ) async throws -> String? = { _ in nil }
    
    public var parseOGImageURL: @Sendable (
        _ url: URL
    ) async throws -> String? = { _ in nil }
}
  • og 이미지, 제목 파싱을 분리하면서, SwiftSoupClientDependecies 주입이 복잡해질 가능성이 있어, 내부 로직은 SwiftSoupProvider로 분리하였습니다.
  • Completion 방식 자체가 가독성이 떨어지기도 하고, 이미지, 제목으로 분리 했다보니 코드 가독성이 더 떨어질 가능성이 있어, 기존 Compleion 방식에서 async/await 방식으로 바꿨습니다.
return .run { send in
    /// - 링크에 대한 메타데이터의 제목 및 썸네일 항목 파싱
    async let title = swiftSoup.parseOGTitle(url)
    async let imageURL = swiftSoup.parseOGImageURL(url)
    try await send(
        .inner(.메타데이터_조회_반영(title: title, imageURL: imageURL)),
        animation: .pokitDissolve
    )
}

FeatureContentCard모듈, ConentCardFeature, ContentCardView 추가하였습니다.

  • 현재 PokitLinkCard 컴포넌트는 디자인 시스템에 있다보니 비지니스 로직을 넣을 수 가 없습니다.
    또한, 리스트를 탐색해서 이미지 url 파싱을 다시 하면, 링크 개수가 많이지면 많아 질 수 록 비용이 많이 들 가능성이 있습니다.
    그래서, 리스트 항목 단위로 이미지 오류을 판단하고 재조회하는 로직을 넣기 위해, ConentCardFeature, ContentCardView를 추가하였습니다.
  • RemindFeature에는 추천링크 3개, 즐겨찾기 링크 3개, 읽지않음 링크 3개가 고정이기 때문에, RemindFeature만 예외적으로 이미지 url 파싱 로직을 Reducer안에 바로 넣었습니다.

PokitLinkCard 컴포넌트 추가 로직 작성하였습니다.

  • 이미지 조회오류를 판단하고 로직을 실행시키기 위해 fetchMetaData라는 함수를 추가적으로 받습니다.
  • LazyImagephaseerror가 발생했을때의 뷰의 onAppear 시점으로 이미지 url 파싱을 진행합니다.
LazyImage(url: url) { phase in
  Group {
      if let image = phase.image {
          image
              .resizable()
              .aspectRatio(contentMode: .fill)
      } else if phase.error != nil {
          placeholder
              .onAppear { fetchMetaData?() }
      } else {
          placeholder
      }
...

문제점

  • 현재로써는 이미지 조회 -> 오류 -> 이미지 url 파싱 -> 이미지 조회로 가는 로직이지만, 여전히 문제는 있습니다.
    인스타그램을 기준으로, 여러번 테스트를 해보니, 짧은 시간에 너무 많은 요청을 인스타그램으로 보내면, ip 단위로 요청을 막아버리는 것 같습니다.
    인스타그램, 페이스북에서는 이러한 직접적인 파싱을 권장하지 않고, 자체 API를 제공하고 있습니다.
  • (결론, 현재 변경사항도 임시방편이다..)

close #152

@ShapeKim98 ShapeKim98 added the Bug 🔫 현재 발견된 버그를 수정하기 위함 label Nov 18, 2024
@ShapeKim98 ShapeKim98 requested a review from stealmh November 18, 2024 06:46
@ShapeKim98 ShapeKim98 self-assigned this Nov 18, 2024
Comment on lines 194 to 203
ForEach(unclassifiedContents) { content in
let isFirst = content == unclassifiedContents.first
let isLast = content == unclassifiedContents.last

PokitLinkCard(
link: content,
action: { send(.컨텐츠_항목_눌렀을때(content)) },
kebabAction: { send(.미분류_케밥_버튼_눌렀을때(content)) }
ForEachStore(
store.scope(state: \.contents, action: \.contents)
) { store in
let isFirst = store.state.id == self.store.contents.first?.id
let isLast = store.state.id == self.store.contents.last?.id

ContentCardView(
store: store,
isFirst: isFirst,
isLast: isLast
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ForEachStore대신 ForEach로 할 수 있으니까 수정해주세욥.
수정한 방법이 soft-deprecated되었음..

Copy link
Contributor Author

@ShapeKim98 ShapeKim98 Nov 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ForEachStore대신 ForEach로 할 수 있으니까 수정해주세욥. 수정한 방법이 soft-deprecated되었음..

엇 deprecated된게 맞을텐데.. 자꾸 컴파일 오류가 나서 이렇게 하긴 했는데.. 다시 한번 해보겠습니다..!!

Comment on lines +11 to +43
final class SwiftSoupProvider {
func parseOGTitle(_ url: URL) async throws -> String? {
try await parseOGMeta(url: url, type: "og:title")
}

func parseOGImageURL(_ url: URL) async throws -> String? {
try await parseOGMeta(url: url, type: "og:image")
}

func parseOGMeta(url: URL, type: String) async throws -> String? {
let html = try String(contentsOf: url)
let document = try SwiftSoup.parse(html)

if let metaData = try document.select("meta[property=\(type)]").first()?.attr("content") {
return metaData
} else {
var request = URLRequest(url: url)
request.setValue(
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36",
forHTTPHeaderField: "User-Agent"
)

let (data, _) = try await URLSession.shared.data(for: request)
guard let html = String(data: data, encoding: .utf8) else {
return nil
}
let document = try SwiftSoup.parse(html)
let metaData = try document.select("meta[property=\(type)]").first()?.attr("content")

return metaData
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

provider 분리하니까 훨낫네요 뷰에 저 코드들 있던것으로 기억하는데 잘 뺀듯

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

provider 분리하니까 훨낫네요 뷰에 저 코드들 있던것으로 기억하는데 잘 뺀듯

감사합니다

/// - Reducer body
public var body: some ReducerOf<Self> {
Reduce(self.core)
._printChanges()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

요런거 까먹지 말구 지워서 올리기
저도 몇개 남겨놧던것같음 ㅋㅋ; 같이지워주세엽

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

요런거 까먹지 말구 지워서 올리기 저도 몇개 남겨놧던것같음 ㅋㅋ; 같이지워주세엽

넵!

Comment on lines -54 to +55
var contents: IdentifiedArrayOf<BaseContentItem>? {
guard let contentList = domain.contentList.data else {
return nil
}
var identifiedArray = IdentifiedArrayOf<BaseContentItem>()
contentList.forEach { content in
identifiedArray.append(content)
}
return identifiedArray
}
var contents: IdentifiedArrayOf<ContentCardFeature.State> = []
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

�굿굿!

Copy link
Contributor

@stealmh stealmh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

고생많았습니다~~!
뭔가 모듈이 많아진 느낌이지만 코드를 보면 너무 잘 분리해놔서 잘 정리해주신 것 같습니다. 수정사항 간단한것들 몇개 있으니 요것들만 해결해주시면 될 것 같습니다!

궁금한거는 인스타 API가 있다고해서, 해당 API를 호출하는 방법으로는 해결이 안되는건지 궁금합니다
파싱이 사실 정상적인 방법은 아니라고 생각이 들긴하는듯..?

@ShapeKim98
Copy link
Contributor Author

고생많았습니다~~! 뭔가 모듈이 많아진 느낌이지만 코드를 보면 너무 잘 분리해놔서 잘 정리해주신 것 같습니다. 수정사항 간단한것들 몇개 있으니 요것들만 해결해주시면 될 것 같습니다!

궁금한거는 인스타 API가 있다고해서, 해당 API를 호출하는 방법으로는 해결이 안되는건지 궁금합니다 파싱이 사실 정상적인 방법은 아니라고 생각이 들긴하는듯..?

모듈이 많아진 느낌이라 정말 고민 많이 했습니다... 그래서 더 늦어진 것도 있는거 같아요...
API로 해결을 해볼까 했는데.. API자체에도 개발자 계정당 조회 갯수 제한이 있는걸로 알고 있기도 하고, 인스타그램만 분기로 처리하기엔 아직 까지는 오버엔지니어링 느낌이 많이 들어서 일단 소개만 했습니다..ㅎㅎ

@stealmh
Copy link
Contributor

stealmh commented Nov 18, 2024

고생많았습니다~~! 뭔가 모듈이 많아진 느낌이지만 코드를 보면 너무 잘 분리해놔서 잘 정리해주신 것 같습니다. 수정사항 간단한것들 몇개 있으니 요것들만 해결해주시면 될 것 같습니다!
궁금한거는 인스타 API가 있다고해서, 해당 API를 호출하는 방법으로는 해결이 안되는건지 궁금합니다 파싱이 사실 정상적인 방법은 아니라고 생각이 들긴하는듯..?

모듈이 많아진 느낌이라 정말 고민 많이 했습니다... 그래서 더 늦어진 것도 있는거 같아요... API로 해결을 해볼까 했는데.. API자체에도 개발자 계정당 조회 갯수 제한이 있는걸로 알고 있기도 하고, 인스타그램만 분기로 처리하기엔 아직 까지는 오버엔지니어링 느낌이 많이 들어서 일단 소개만 했습니다..ㅎㅎ

아....횟수제한이면 어쩔수없네요 😂 지금 할 수 있는 최선을 한거였네..! 채고입니다

@ShapeKim98 ShapeKim98 merged commit 00a99c7 into develop Nov 18, 2024
1 check passed
@stealmh stealmh linked an issue Nov 22, 2024 that may be closed by this pull request
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug 🔫 현재 발견된 버그를 수정하기 위함
Projects
None yet
Development

Successfully merging this pull request may close these issues.

일부 url에서 이미지 조회가 안되는 문제
2 participants