
Repository URL : https://github.com/jiyean99/order-system-fe
Vuetify의 v-form, v-card, v-text-field를 활용해 깔끔한 상품 등록 UI를 구성함.
<v-text-field label="상품명" v-model="name"/>
<v-text-field label="카테고리" v-model="category"/>
<v-text-field label="가격" v-model.number="price"/>
<v-text-field label="재고수량" v-model.number="stockQuantity"/>
<v-file-input
label="상품이미지"
@change="handleFileUpload"
multiple
/>
핵심:
v-model.number: 숫자 입력 시 자동으로 Number 타입 변환v-file-input: 파일 업로드 처리, multiple로 다중 선택 가능handleFileUpload(event) {
// event.target.files[0]으로 첫 번째 파일만 선택
this.productImage = event.target.files[0];
}
event.target.files는 HTML <input type="file">의 FileList 객체임이미지 업로드 시 JSON으로는 전송 불가하므로 FormData 객체를 사용함.
async createProduct() {
const data = new FormData();
data.append("name", this.name);
data.append("category", this.category);
data.append("price", this.price);
data.append("stockQuantity", this.stockQuantity);
// 이미지 필수 아님 → 백엔드에서 null 허용 처리
if (this.productImage) {
data.append("productImage", this.productImage);
}
await axios.post(`${process.env.VUE_APP_API_BASE_URL}/product/create`, data);
this.$router.push("/product/list");
}
JSON: { name: "상품1", image: FileObject } ❌ 불가능
FormData: name=상품1&productImage=File ✅ 가능
중요: productImage를 append하지 않으면 백엔드 DTO에서 null로 매핑되지만, null을 명시적으로 append하면 타입 에러 발생하므로 조건부 처리함.
main.js에서 이미 설정한 Axios Interceptor가 자동으로 토큰을 포함함.
// 자동으로 적용됨
config.headers['Authorization'] = `Bearer ${accessToken}`;
// Content-Type은 FormData일 때 브라우저가 자동으로 multipart/form-data로 설정
public class ProductCreateDto {
private String name;
private String category;
private Integer price;
private Integer stockQuantity;
private MultipartFile productImage; // null 허용
}
@PostMapping(value = "/create", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<?> create(@Valid @RequestPart ProductCreateDto dto) {
// productImage가 null이어도 정상 처리
}
현재는 간단한 console.log(e)로 처리했으나 실제로는:
try {
await axios.post(...);
this.$router.push("/product/list");
} catch (e) {
if (e.response?.status === 400) {
alert("입력값을 확인해주세요");
} else {
alert("상품 등록에 실패했습니다");
}
}
1. 폼 입력 → v-model로 data 바인딩
2. 파일 선택 → handleFileUpload → productImage 저장
3. [등록] 클릭 → FormData 생성 → /product/create POST
4. 성공 → 상품목록 페이지 이동
5. 실패 → 콘솔 출력 (개선 필요)
이 구현으로 이미지 포함 상품 등록이 완성됨. FormData의 조건부 append 처리와 v-model.number 활용이 핵심 포인트임.
