반응형

API 호출 후 응답까지 걸리는 시간이 길면 보통 ProgressBar로 진행정도를 표시해주고는 한다. 

API 호출 후 응답이 언제 올지 어떻게 알고 진행 정도를 표시해 줄 수 있을까? 

onUploadProgress 

파일 업로드 중 진행 상태를 모니터링할 수 있는 콜백 함수

파일 업로드 과정에서 특정 간격으로 실행된다. 

progressEvent 객체를 매개변수로 받아서 파일 업로드의 진행 상태를 알 수 있는 정보를 제공해준다. 

 

 

progressEvent 객체

loaded와 total을 확인할수 있다. 

loaded: 현재까지 업로드된 데이터 크기 (바이트 단위)

total: 업로드 할 총 데이터 크기 (바이트 단위, 일부 서버 환경에서는 제공되지 않을 수 있다. )

 

//serviceApis.js
const uploadReport = (serviceId, formData, onUploadProgress) => {
    return axiosInstance.post(`/services/${serviceId}/report/upload`, formData
        , {
            headers: {
                "Content-Type": "multipart/form-data",
            },
            onUploadProgress
        }
    );
};

// API 호출부분
const res = await uploadReport(id, formData, (progressEvent) => {
    const totalLength = progressEvent.total;
    if (totalLength) {
        const progress = Math.round((progressEvent.loaded * 100) / totalLength);
        setPercentage(progress);
        console.log("진행 %: ", progress);
    }
});

 

progresss가 100이되면서 끝나게 된다. 

 

이 progress를 rc-progress와 같은 라이브러리를 이용해서 로딩바로 구현해볼수 있다. 

import {Circle} from "rc-progress";


     <Circle percent={percentage} //setPercentage()로 설정한 진행%
            strokeWidth="3"
            strokeColor="#D3D3D3"/>

'develop > JavaScript' 카테고리의 다른 글

POST로 파일, 이미지, JSON 데이터 보내기  (0) 2024.11.08
[Javascript] 에러 핸들링  (0) 2024.09.20
반응형

백엔드 코드

특정 서비스에 사진파일 여러개( attaches ), PDF 파일 1개( report )와 정보( ServiceDTO.ReportUpload  )를 저장해야한다. 

API 호출시 참고할 백엔드 코드이다. 

POST 라는것과 어떤 타입으로 보내야할지 알 수 있다. 

@PostMapping("/services/{id}/upload")
public ResponseEntity<Void> uploadReport(@PathVariable(value = "id") Long id, 
                 @RequestPart ServiceDTO.ReportUpload request, 
                 @RequestPart(value = "report") MultipartFile report, 
                 @RequestPart(value = "attaches") MultipartFile[] attaches) throws IOException { 

 }



//ServiceDTO.java 파일로 이동해서 확인해보면 아래와 같다. 
@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public static class ReportUpload {
    private String date;
    private String time;
    private double degree;
    // 생략
}

@PathVariable 

API 호출시 Path(URL)에 포함되는 변수
URL에서 'id'라고 쓰여진 부분에 넣으면 된다. 

 

@RequestPart

multipart/form-data 형식에서 파일이나 폼 데이터를 받기 위해 사용
'report''attaches'는 파일 업로드 파라미터로 다뤄진다. 
MultipartFile(단일 파일), MultipartFile[] (다중 파일) 로 받는다는 뜻이다.

 

'request'라는 이름의 파라미터로 ServiceDTO.ReportUpload 객체 형태의 데이터를 받는다. 

ServiceDTO.ReportUpload 객체 내부의 특정 필수 데이터가 없거나 이름이 틀리거나, 타입(문자열로 보내야하는데 숫자로 보내거나) 이 틀리면 에러가 발생할 수 있다. 

백단 설정에 따라 자세한 에러메시지를 보내줄수도, 보내주지 않을수도 있기때문에 에러 메시지를 확인했을때 원인을 알수 없다면 API 개발 담당자가 로그를 확인해볼수 있도록 해야한다. 

 

 

프론트단 코드

화면에서 사용자가 입력한 데이터를 아래와 같이 모으고, API에 함께 보내면 된다. 

const serviceId = 99;
const uploadData = {
    date: '2024-11-08',
    time: '11:00',
    degree: 11,
}
const pdfFiles = {파일객체 한개}; // PDF 파일
const attacheFiles = [파일객체 여러개],  // 이미지 파일

// 폼데이터에 저장
const reportData =  new FormData();
reportData.append('request',  JSON.stringify(uploadData));
reportData.append('report', pdfFiles);
attacheFiles.forEach((file, index) => {
  reportData.append("attaches", file); 
});


// 업로드 API 호출 
axios.post(`/services/${serviceId}/upload`, reportData, {
  headers: {
    "Content-Type": "multipart/form-data",
  },
})
.then(response => {
  console.log("업로드 성공:", response);
})
.catch(error => {
  console.error("업로드 실패:", error);
});

'develop > JavaScript' 카테고리의 다른 글

axios의 onUploadProgress (요청 응답 시간 확인)  (0) 2024.11.08
[Javascript] 에러 핸들링  (0) 2024.09.20
반응형

Exception과 Error

Exception

- 예상 불가능한 오류 상황

- 파일을 정상적으로 읽어오지 못했거나, 네트워크 오류, 메모리 부족 등

- 발생시 프로그램 실행이 중단된다. 

 

Error

- 예상 가능한 Error State(4XX, 5XX 에러)

- 일반적으로 프로그램 실행중 발생 가능한 문제를 나타내는 객체

- 다양한 Error객체가 존재한다. (TypeError, ReferenceError, SyntaxError 등)

- 시스템 에러, 메모리 에러, 문법 에러 등 

 

발생 가능한 예외에 대해 무작정 try-catch문이나 throw new Error()로 예외처리를 하기보다는

예상 가능한 예외라면 Error State를 정의해서 예상 가능한 에러 상황으로 간주해서 상황에 맞춰 각기 다른 처리를 해주는것이 좋다. 

 

문제가 발생하지 않을 상황(happy path)만을 가정하고 코드를 짠 뒤에 그 외의 상황에서 발생하는 에러는 모두 예외로 생각하고 예외 처리를 해야하는 곳에서 제대로 처리하지 않는다면, 에러가 상위 레벨로 계속 던져질것이다.  프로그램 안정성과 사용성에 문제가 있을뿐더러 유지보수에도 좋지 않다. 

에러 핸들링 방법

1. try-catch문을 사용

try {
    // 에러가 발생할 가능성이 있는 코드
    let result = riskyFunction();
} catch (error) {
    // 에러 처리 코드
    console.error("오류 발생:", error.message);
} finally {
    // 항상 실행되는 코드 (선택적)
    console.log("처리 완료");
}

 

2. throw문 사용

예상치 못한 에러인 경우 throw new Error()를 사용할수있다. 

특정 조건에서 에러를 수동으로 발생시키고자 할때 사용

function checkValue(value) {
    if (value < 0) {
        throw new Error("값은 0 이상이어야 합니다.");
    }
    return value;
}

try {
    checkValue(-1);
} catch (error) {
    console.error(error.message);
}

 

3. Promise와 async/await에서 에러 핸들링

비동기 함수 에러 처리시 try-catch문을 사용 

async function fetchData() {
    try {
        let response = await fetch('https://api.example.com/data');
        if (!response.ok) {
            throw new Error('네트워크 응답이 좋지 않습니다.');
        }
        let data = await response.json();
        return data;
    } catch (error) {
        console.error("데이터를 가져오는 중 오류 발생:", error.message);
    }
}

 

 

에러 핸들링 해야하는 상황

에러핸들리은 에러 발생 가능성이 있는 부분에서 사용한다. 

예를 들어 API호출, 파일 입출력, DB 쿼리외부와 상호작용하는 부분에서 사용할수있을것이다. 

에러 발생 가능한 부분을 try로 감싼다. 

try {
    let response = await fetch('https://api.example.com/data');
    let data = await response.json();
} catch (error) {
    console.error("API 호출 중 오류 발생:", error.message);
}

 

 

비동기 작업에서 에러를 처리

async function fetchData() {
    try {
        let response = await fetch('https://api.example.com/data');
        if (!response.ok) {
            throw new Error('네트워크 오류');
        }
        let data = await response.json();
        return data;
    } catch (error) {
        console.error("데이터 가져오기 중 오류 발생:", error.message);
    }
}

 

또한 자원을 연결하고 해제할때

let connection;
try {
    connection = openDatabaseConnection();
    // 데이터베이스 작업 수행
} catch (error) {
    console.error("데이터베이스 오류:", error.message);
} finally {
    if (connection) {
        closeDatabaseConnection(connection);
    }
}

 

 

 

class Connection {
  attempt(): void {
    throw new Error('인터넷에 연결할 수 없습니다!');
  }
}

class AuthenticationService {
  constructor(private connection: Connection) { }

  authenticate() {
    try {
      this.connection.attempt();
      // 인증 로직...
    } catch (error) {
      console.error("인증 중 오류 발생:", error.message);
      // 사용자에게 오류 메시지 표시
    }
  }
}

class Application {
  constructor(private authService: AuthenticationService) { }

  start() {
    try {
      this.authService.authenticate();
    } catch (error) {
      console.error("애플리케이션 실행 중 오류 발생:", error.message);
      // 사용자에게 오류 메시지 표시
    }
  }
}

// 인스턴스 생성 및 실행
const connection = new Connection();
const authService = new AuthenticationService(connection);
const app = new Application(authService);
app.start();

 

Connection클래스의 attempt() 함수는 인터넷 연결이 되지 않은 상황을 가정해서 에러를 던지도록 만들어져있다. 

그래서 attempt()함수를 사용하는 부분은 모두 try-catch로 에러핸들링을 하였다. 

+ Recent posts