스토어에도 같은걸 추가해야 한다고 한다.
package com.sparta.dianomi.domain.store.service
import com.sparta.dianomi.common.exception.ModelNotFoundException
import com.sparta.dianomi.domain.store.dto.CreateStoreDto
import com.sparta.dianomi.domain.store.dto.StoreResponseDto
import com.sparta.dianomi.domain.store.dto.UpdateStoreDto
import com.sparta.dianomi.domain.store.model.Store
import com.sparta.dianomi.domain.store.repository.StoreRepository
import org.springframework.data.repository.findByIdOrNull
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
@Service
class StoreServiceImpl(
private val storeRepository: StoreRepository,
):StoreService {
override fun getStore(storeId: Long): StoreResponseDto {
val findStore = storeRepository.findByIdOrNull(storeId)?: throw ModelNotFoundException("store", storeId)
//데이터를 찾지 못함
return findStore.toResponse();
//dto를 제외하고 클래스를 호출할 생각 X 하면 의존성 증가.
}
//접근하는 user가 상점의 특정정보를 들고있어야 대조해보고
override fun gerStoreList(): List<StoreResponseDto> {
return storeRepository.findAll().map{it.toResponse()}
}
@Transactional
override fun createStore(createStoreDto: CreateStoreDto, userId: Long): StoreResponseDto {
return storeRepository.save(
Store(
name = createStoreDto.name,
address = createStoreDto.address,
businessNum = createStoreDto.businessNum,
description = createStoreDto.description,
userId = userId
)
).toResponse()
}//상호명 같은거 처리해줘야하는지?
@Transactional
override fun updateStore(storeId: Long, updateStoreDto: UpdateStoreDto, userId: Long): StoreResponseDto {
_checkOwner(storeId, userId)
val findStore = storeRepository.findByIdOrNull(storeId)?: throw ModelNotFoundException("store", storeId)
//수정하는 user와 store를 해서
extracted(findStore, updateStoreDto)
// 함수로 바꾸면 entity안에 ?!
return storeRepository.save(findStore).toResponse()
}
private fun extracted(
findStore: Store,
updateStoreDto: UpdateStoreDto
) {
findStore.name = updateStoreDto.name
findStore.address = updateStoreDto.address
findStore.businessNum = updateStoreDto.businessNum
findStore.description = updateStoreDto.description
}
@Transactional
override fun deleteStore(storeId: Long, userId: Long) {
_checkOwner(storeId, userId)
val findStore = storeRepository.findByIdOrNull(storeId)?: throw ModelNotFoundException("store", storeId)
storeRepository.delete(findStore)
}
private fun _checkOwner(storeId: Long,userId: Long) {
val store : Store =storeRepository.findByIdOrNull(storeId) ?: throw Exception("Store not found");
if (store.userId != userId) {
throw Exception("User does not have permission to modify this store")
}
}
}
이렇게 스토어의 컨트롤러도 메뉴처럼 만든다. 하지만 createstore에는 _checkOwner를 넣지 않는데, 그 이유는 아직 생성이 안됬기 때문이다.
package com.sparta.dianomi.domain.store.controller
import com.sparta.dianomi.authority.security.UserPrincipal
import com.sparta.dianomi.domain.store.dto.CreateStoreDto
import com.sparta.dianomi.domain.store.dto.StoreResponseDto
import com.sparta.dianomi.domain.store.dto.UpdateStoreDto
import com.sparta.dianomi.domain.store.service.StoreService
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.security.access.prepost.PreAuthorize
import org.springframework.security.core.annotation.AuthenticationPrincipal
import org.springframework.web.bind.annotation.DeleteMapping
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.PutMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
@RequestMapping("/stores")
@RestController
class StoreController (
private val storeService: StoreService
){
@GetMapping("/{storeId}")
fun getStore(
@PathVariable storeId:Long
):ResponseEntity<StoreResponseDto>{
return ResponseEntity
.status(HttpStatus.OK)
.body(storeService.getStore(storeId))
}
//가게 단건 조회
@GetMapping
fun getStoreList():ResponseEntity<List<StoreResponseDto>>{
return ResponseEntity
.status(HttpStatus.OK)
.body(storeService.gerStoreList())
}
//가게 리스트
@PostMapping
@PreAuthorize("hasRole('STORE')")//스토어인 유저만 만들수있슴
fun createStore(
@RequestBody createStoreDto: CreateStoreDto,
@AuthenticationPrincipal user: UserPrincipal
):ResponseEntity<StoreResponseDto>{
return ResponseEntity
.status(HttpStatus.CREATED)
.body(storeService.createStore(createStoreDto))
}
//가게 정보 생성
@PutMapping("/{storeId}")
@PreAuthorize("hasRole('STORE')")
fun updateStore(
@PathVariable storeId: Long,
@RequestBody updateStoreDto: UpdateStoreDto,
@AuthenticationPrincipal user: UserPrincipal
):ResponseEntity<StoreResponseDto>{
return ResponseEntity
.status(HttpStatus.OK)
.body(storeService.updateStore(storeId,updateStoreDto))
}
//가게 정보 수정
@DeleteMapping("/{storeId}")
@PreAuthorize("hasRole('STORE')")
fun deleteStore(
@PathVariable storeId:Long,
@AuthenticationPrincipal user: UserPrincipal
):ResponseEntity<Unit>{
storeService.deleteStore(storeId)
return ResponseEntity
.status(HttpStatus.NO_CONTENT)
.build()
}
//가게 정보 삭제
}
이랬던 녀석은
package com.sparta.dianomi.domain.store.controller
import com.sparta.dianomi.authority.security.UserPrincipal
import com.sparta.dianomi.domain.store.dto.CreateStoreDto
import com.sparta.dianomi.domain.store.dto.StoreResponseDto
import com.sparta.dianomi.domain.store.dto.UpdateStoreDto
import com.sparta.dianomi.domain.store.service.StoreService
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.security.access.prepost.PreAuthorize
import org.springframework.security.core.annotation.AuthenticationPrincipal
import org.springframework.web.bind.annotation.DeleteMapping
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.PutMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
@RequestMapping("/stores")
@RestController
class StoreController (
private val storeService: StoreService
){
@GetMapping("/{storeId}")
fun getStore(
@PathVariable storeId:Long
):ResponseEntity<StoreResponseDto>{
return ResponseEntity
.status(HttpStatus.OK)
.body(storeService.getStore(storeId))
}
//가게 단건 조회
@GetMapping
fun getStoreList():ResponseEntity<List<StoreResponseDto>>{
return ResponseEntity
.status(HttpStatus.OK)
.body(storeService.gerStoreList())
}
//가게 리스트
@PostMapping
@PreAuthorize("hasRole('STORE')")//스토어인 유저만 만들수있슴
fun createStore(
@RequestBody createStoreDto: CreateStoreDto,
@AuthenticationPrincipal user: UserPrincipal
):ResponseEntity<StoreResponseDto>{
return ResponseEntity
.status(HttpStatus.CREATED)
.body(storeService.createStore(createStoreDto,user.id))
}
//가게 정보 생성
@PutMapping("/{storeId}")
@PreAuthorize("hasRole('STORE')")
fun updateStore(
@PathVariable storeId: Long,
@RequestBody updateStoreDto: UpdateStoreDto,
@AuthenticationPrincipal user: UserPrincipal
):ResponseEntity<StoreResponseDto>{
return ResponseEntity
.status(HttpStatus.OK)
.body(storeService.updateStore(storeId,updateStoreDto, user.id))
}
//가게 정보 수정
@DeleteMapping("/{storeId}")
@PreAuthorize("hasRole('STORE')")
fun deleteStore(
@PathVariable storeId:Long,
@AuthenticationPrincipal user: UserPrincipal
):ResponseEntity<Unit>{
storeService.deleteStore(storeId, user.id)
return ResponseEntity
.status(HttpStatus.NO_CONTENT)
.build()
}
//가게 정보 삭제
}
이렇게 곳곳에 user.id를 집어넣는다.
interface StoreService {
fun getStore(storeId:Long):StoreResponseDto
//가게정보조회
fun gerStoreList():List<StoreResponseDto>
fun createStore(createStoreDto: CreateStoreDto, userId: Long):StoreResponseDto
//신규상점입점
fun updateStore(storeId: Long,updateStoreDto: UpdateStoreDto, userId: Long):StoreResponseDto
//상점정보수정
fun deleteStore(storeId: Long, userId: Long)
서비스도 이렇게 userid를 넣는다. 하지만 dto는 건드리지 않는다!
그러자 정상작동한다!!! 안됬던 이유는, store랑 menu는 일심동체라서 둘다 바꿔야만 하는거였다. 하나만 바꾸니 안돴던거다.
eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMyIsImlzcyI6InRlYW0uc3BhcnRhLmNvbSIsImlhdCI6MTcwNjE3OTM4MiwiZXhwIjoxNzA3MDQzMzgyLCJyb2xlIjoiVVNFUiIsIm5pY2tOYW1lIjoibmlja25hbWUifQ.gkEkJPzVWkvZvNb5YsrxGO3T91LthvRBlbjcph38O2I
유저 저장용
그런데 get store가 Can not set long field cohttp://m.sparta.dianomi.domain.store.model.Store.userId to null value라는 오류를 뱉어내는게 아닌가? 머리를 싸매고 궁리한 결과, 팀장님이 userid는 나중에 추가한거라 데이터베이스에 userid부분이 그 전에 생성한 녀석들이 null값을 가지고 있어서 그런거라고 하면서 그전에 생성한 목록들을 날려버리자 정상작동했다.
카테고리 추가법
enum class Category {
족발가게, 햄버거가게, 치킨가게
}
이걸 추가하고
스토어컨트롤러에
@GetMapping("/categories")
fun getCategories(): List<Category> {
return Category.entries
}
그러면
[
"족발가게",
"햄버거가게",
"치킨가게"
]
@GetMapping("/categories")
fun getCategories(): Map<String, List<String>> {
return mapOf("data" to Category.entries.map { it.name })
}
@ManyToOne이나 @OneToMany는 @Column 필요없음!
cart작업을 했는데, OndToMany, ManyToOne을 넣었더니
2024-01-25T17:28:07.210+09:00 ERROR 15816 --- [nio-8080-exec-2] o.a.c.c.C.[Tomcat].[localhost] : Exception Processing ErrorPage[errorCode=0, location=/error]
jakarta.servlet.ServletException: Unable to handle the Spring Security Exception because the response is already committed. 이거랑 2024-01-25T17:28:07.209+09:00 ERROR 15816 --- [nio-8080-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] threw exception
org.springframework.security.access.AccessDeniedException: Access Denied이거랑 2024-01-25T17:28:07.205+09:00 ERROR 15816 --- [nio-8080-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: Infinite recursion (StackOverflowError)] with root cause
java.lang.StackOverflowError: null이거랑 2024-01-25T17:28:07.203+09:00 WARN 15816 --- [nio-8080-exec-2] .w.s.m.s.DefaultHandlerExceptionResolver : Failure while trying to resolve exception [org.springframework.http.converter.HttpMessageNotWritableException]
java.lang.IllegalStateException: Cannot call sendError() after the response has been committed
at org.apache.catalina.connector.ResponseFacade.checkCommitted(ResponseFacade.java:485) ~[tomcat-embed-core-10.1.17.jar:10.1.17]
at org.apache.catalina.connector.ResponseFacade.sendError(ResponseFacade.java:337) ~[tomcat-embed-core-10.1.17.jar:10.1.17]
이렇게 에러가 너무 많이 떠서 기겁하고 어떻게든 해결하려고 애쓴결과, 무한참조가 발생해서 생긴 문제였다.무한참조는
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator::class, property = "id")
이녀석을 @ManyToOne이나 @OneToMany을 한곳마다 박아넣어서 해결했다.
그리고
data class CartDto(
val id: Long,
val menuId: Long,
val storeId: Long,
val memberId: Long,
val count: Int
) {
companion object {
fun from(cart: Cart): CartDto {
return CartDto(
id = cart.id,
menuId = cart.menu.id,
storeId = cart.store.id,
memberId = cart.member.id,
count = cart.count
)
}
}
}
에도 오류가 발생했는데, 컴페니언 오브젝트에서 id쪽이 빨간줄이 떳는데, 필요한 항목 Long 발견된 항목 Long?이라길래 저위의 long를 전부 ?를 붙였다.
그 다음에는 카트 삭제나 조회에 다른 번호를 입력해 실패했을때 인증실패란 문구가 나오길래 인증 실패가 아닌데 그런 문구가 나오는게 어색해서
@RestController
@RequestMapping("/carts")
class CartController(private val cartService: CartService) {
@PostMapping
fun addToCart(@AuthenticationPrincipal user: UserPrincipal, @RequestBody cartCreateDto: CartCreateDto): ResponseEntity<CartDto> {
val memberId = user.id
val cart = cartService.addToCart(cartCreateDto.menuId, cartCreateDto.storeId, memberId, cartCreateDto.count)
return ResponseEntity.status(HttpStatus.CREATED).body(CartDto.from(cart))
}
@DeleteMapping("/{cartId}")
fun removeFromCart(@AuthenticationPrincipal user: UserPrincipal, @PathVariable cartId: Long): ResponseEntity<String> {
val memberId = user.id
return try {
cartService.removeFromCart(memberId, cartId)
ResponseEntity.ok("성공적으로 제거하였습니다.")
} catch (e: Exception) {
ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("제거에 실패했습니다.")
}
}
@DeleteMapping("/member/{memberId}")
fun clearCart(@AuthenticationPrincipal user: UserPrincipal, @PathVariable memberId: String): ResponseEntity<String> {
val memberId = user.id
return try {
cartService.clearCart(memberId)
ResponseEntity.ok("성공적으로 제거하였습니다.")
} catch (e: Exception) {
ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("제거에 실패했습니다.")
}
}
@GetMapping("/{cartId}")
fun getCart(@AuthenticationPrincipal user: UserPrincipal, @PathVariable cartId: Long): ResponseEntity<out Any> {
val memberId = user.id
return try {
val cart = cartService.getCart(memberId, cartId)
if (cart.member.id != memberId) {
ResponseEntity.status(HttpStatus.FORBIDDEN).body("You do not have permission to view this cart.")
} else {
ResponseEntity.ok(CartDto.from(cart))
}
} catch (e: Exception) {
ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("조회할 수 없습니다.")
}
}
}
try catch로 성공, 실패 문구를 넣었다. 이게 훨씬 보기에도 깔끔하고 이제 인증 실패도 나오지 않는다.