-
Notifications
You must be signed in to change notification settings - Fork 0
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
feature: 배너 이미지 조회 #18
The head ref may contain hidden characters: "16-feature-\uBC30\uB108-\uC774\uBBF8\uC9C0-\uC870\uD68C"
Changes from 8 commits
a4e94bb
d9997e4
78df0f3
e4116e0
417bf03
7c74c6b
0b66947
bec1f0b
28769f2
b938c23
3160c34
c3422fc
9db6793
18ffcc5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,8 +2,6 @@ name: CI With Pull Request | |
|
||
on: | ||
push: | ||
pull_request: | ||
types: [opened, reopened] | ||
|
||
jobs: | ||
build: | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package com.petqua.application.banner | ||
|
||
import com.petqua.domain.banner.Banner | ||
import java.time.LocalDateTime | ||
|
||
data class FindBannerResult( | ||
val id: Long, | ||
val imageUrl: String, | ||
val linkUrl: String, | ||
val createAt: LocalDateTime, | ||
val updateAt: LocalDateTime, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. createAt, updateAt 도 응답하네요! 클라이언트에게 아마도 필요하지 않을 정보를 응답하는 이유가 궁금합니다 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 문제없는 범위 내에서 createdAt을 자주 응답하긴 했었습니다.. 추후에 추가되는 경우가 종종 있었거든요! |
||
) { | ||
companion object { | ||
fun from(banner: Banner): FindBannerResult { | ||
return FindBannerResult( | ||
id = banner.id, | ||
imageUrl = banner.imageUrl, | ||
linkUrl = banner.linkUrl, | ||
createAt = banner.createdAt, | ||
updateAt = banner.updatedAt, | ||
) | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package com.petqua.application.banner | ||
|
||
import com.petqua.domain.banner.BannerRepository | ||
import org.springframework.cache.annotation.Cacheable | ||
import org.springframework.stereotype.Service | ||
import org.springframework.transaction.annotation.Transactional | ||
|
||
@Transactional | ||
@Service | ||
class BannerService( | ||
private val bannerRepository: BannerRepository, | ||
) { | ||
|
||
@Cacheable("banners") | ||
Comment on lines
+14
to
+15
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 캐시 좋네요! 이거 나중에 레디스로 이사하나요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 나중에 cacheConfig에서 구현체만 수정하면 될 것 같군요! |
||
@Transactional(readOnly = true) | ||
fun getBannerList(): List<FindBannerResult> { | ||
val banners = bannerRepository.findAll() | ||
return banners.map { FindBannerResult.from(it) } | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package com.petqua.common.cofig | ||
|
||
import org.springframework.cache.CacheManager | ||
import org.springframework.cache.annotation.EnableCaching | ||
import org.springframework.cache.concurrent.ConcurrentMapCacheManager | ||
import org.springframework.context.annotation.Bean | ||
import org.springframework.context.annotation.Configuration | ||
|
||
|
||
@EnableCaching | ||
@Configuration | ||
class CacheConfiguration { | ||
|
||
@Bean | ||
fun cacheManager(): CacheManager { | ||
val cacheManager = ConcurrentMapCacheManager() | ||
cacheManager.setCacheNames(listOf("banners")) | ||
return cacheManager | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package com.petqua.domain.banner | ||
|
||
import com.petqua.common.domain.BaseEntity | ||
import jakarta.persistence.* | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
@Entity | ||
class Banner( | ||
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) | ||
val id: Long = 0L, | ||
|
||
@Column(nullable = false) | ||
val imageUrl: String, | ||
|
||
@Column(nullable = false) | ||
val linkUrl: String, | ||
) : BaseEntity() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package com.petqua.domain.banner | ||
|
||
import org.springframework.data.jpa.repository.JpaRepository | ||
|
||
interface BannerRepository: JpaRepository<Banner, Long> { | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package com.petqua.presentation.banner | ||
|
||
import com.petqua.application.banner.BannerService | ||
import com.petqua.application.banner.FindBannerResult | ||
import org.springframework.http.ResponseEntity | ||
import org.springframework.web.bind.annotation.GetMapping | ||
import org.springframework.web.bind.annotation.RequestMapping | ||
import org.springframework.web.bind.annotation.RestController | ||
|
||
@RequestMapping("/banner") | ||
@RestController | ||
class BannerController( | ||
private val bannerService: BannerService | ||
) { | ||
|
||
@GetMapping | ||
fun getBanners(): ResponseEntity<List<FindBannerResult>> { | ||
val bannerList = bannerService.getBannerList() | ||
return ResponseEntity.ok(bannerList) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
package com.petqua.application.banner | ||
|
||
import com.petqua.domain.banner.Banner | ||
import com.petqua.domain.banner.BannerRepository | ||
import io.kotest.core.spec.style.BehaviorSpec | ||
import io.kotest.matchers.shouldBe | ||
import org.mockito.Mockito.atMost | ||
import org.mockito.Mockito.verify | ||
import org.springframework.boot.test.context.SpringBootTest | ||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment.NONE | ||
import org.springframework.boot.test.mock.mockito.SpyBean | ||
import org.springframework.test.context.TestConstructor | ||
import org.springframework.test.context.TestConstructor.AutowireMode.ALL | ||
|
||
@TestConstructor(autowireMode = ALL) | ||
@SpringBootTest(webEnvironment = NONE) | ||
class BannerServiceTest( | ||
private var bannerService: BannerService, | ||
@SpyBean private var bannerRepository: BannerRepository, | ||
) : BehaviorSpec({ | ||
|
||
Given("Banner 조회 테스트") { | ||
bannerRepository.saveAll( | ||
listOf( | ||
Banner(imageUrl = "imageUrlA", linkUrl = "linkUrlA"), | ||
Banner(imageUrl = "imageUrlB", linkUrl = "linkUrlB"), | ||
Banner(imageUrl = "imageUrlC", linkUrl = "linkUrlC"), | ||
) | ||
) | ||
|
||
When("Banner를 전체 조회 하면") { | ||
val results = bannerService.getBannerList() | ||
|
||
Then("모든 Banner가 조회 된다") { | ||
results.size shouldBe 3 | ||
} | ||
} | ||
|
||
When("Banner가 캐싱 되어 있으면") { | ||
repeat(5) { bannerService.getBannerList() } | ||
|
||
Then("퀴리가 발생 하지 않는다") { | ||
verify(bannerRepository, atMost(1)).findAll() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 꼼꼼 테스트 좋아요 👍 |
||
} | ||
} | ||
} | ||
}) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
package com.petqua.presentation.banner | ||
|
||
import com.petqua.application.banner.FindBannerResult | ||
import com.petqua.domain.banner.Banner | ||
import com.petqua.domain.banner.BannerRepository | ||
import com.petqua.test.ApiTestConfig | ||
import io.restassured.module.kotlin.extensions.Extract | ||
import io.restassured.module.kotlin.extensions.Given | ||
import io.restassured.module.kotlin.extensions.Then | ||
import io.restassured.module.kotlin.extensions.When | ||
import org.assertj.core.api.Assertions.assertThat | ||
import org.springframework.http.HttpStatus | ||
|
||
class BannerControllerTest( | ||
private val bannerRepository: BannerRepository | ||
) : ApiTestConfig() { | ||
init { | ||
Given("배너가 등록되어 있다.") { | ||
val banner = bannerRepository.saveAll( | ||
listOf( | ||
Banner(imageUrl = "imageUrlC", linkUrl = "linkUrlA"), | ||
Banner(imageUrl = "imageUrlB", linkUrl = "linkUrlB") | ||
) | ||
) | ||
|
||
When("배너 목록을 조회한다.") { | ||
val response = Given { | ||
log().all() | ||
} When { | ||
get("/banner") | ||
} Then { | ||
log().all() | ||
} Extract { | ||
response() | ||
} | ||
|
||
Then("배너 목록을 응답한다.") { | ||
val findBannerResponse = response.`as`(Array<FindBannerResult>::class.java) | ||
assertThat(response.statusCode).isEqualTo(HttpStatus.OK.value()) | ||
assertThat(findBannerResponse.size).isEqualTo(2) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. SoftAssertions 안 쓰면 홍고가 화내요ㅠ There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 아 이걸 놓쳤네;;;;;;;;;;; |
||
} | ||
} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package com.petqua.test | ||
|
||
import io.kotest.core.spec.style.BehaviorSpec | ||
import io.restassured.RestAssured | ||
import org.springframework.beans.factory.annotation.Autowired | ||
import org.springframework.boot.test.context.SpringBootTest | ||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT | ||
import org.springframework.boot.test.web.server.LocalServerPort | ||
|
||
@SpringBootTest(webEnvironment = RANDOM_PORT) | ||
abstract class ApiTestConfig : BehaviorSpec() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 조아요~~ |
||
|
||
@LocalServerPort | ||
protected val port: Int = RestAssured.port | ||
|
||
@Autowired | ||
private lateinit var dataCleaner: DataCleaner | ||
|
||
init { | ||
afterContainer { | ||
dataCleaner.clean() | ||
} | ||
} | ||
} | ||
Comment on lines
+10
to
+24
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 저는 너무 좋은데요! 한결 편해질 것 같아요!!! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
도커 공부 해야하는데 언제하죠ㅋㅋㅋㅎ