6 분 소요

project

오늘 한 일(회고)

  • 프로젝트 생각 정리

생각정리 1

project

어제에 이어서 bulkcreate을 활용해서 데이터를 입력하려고 한다.

{
    "doctorId":1,
    "dayOFTheWeek":1,
    "startTime":"10:00",
    "endTime":"18:00",
    "startDate":"2023-03-01",
    "endDate":"2023-03-31"
},
{
    "doctorId":1,
    "dayOFTheWeek":2,
    "startTime":"10:00",
    "endTime":"18:00",
    "startDate":"2023-03-01",
    "endDate":"2023-03-31"

},

{
    "doctorId":1,
    "dayOFTheWeek":3,
    "startTime":"10:00",
    "endTime":"18:00",
    "startDate":"2023-03-01",
    "endDate":"2023-03-31"
}
 

이런 식으로 프론트에서 서버로 데이터를 보내려고 하는데… 어떻게 양식을 짜야할지 고민이 많이 되었다.

<form>
    <div class="form-group">
        <label for="doctorId">의사 ID</label>
        <input type="number" class="form-control" id="doctorId" name="doctorId">
    </div>
    <div class="schedule-group">
        <div class="form-group">
            <label for="dayOFTheWeek">요일</label>
            <select class="form-control" name="dayOFTheWeek">
                <option value="1">월요일</option>
                <option value="2">화요일</option>
                <option value="3">수요일</option>
                <option value="4">목요일</option>
                <option value="5">금요일</option>
                <option value="6">토요일</option>
                <option value="7">일요일</option>
            </select>
        </div>
        <div class="form-group">
            <label for="startTime">시작 시간</label>
            <input type="time" class="form-control" name="startTime" step="1800" required>
        </div>
        <div class="form-group">
            <label for="endTime">종료 시간</label>
            <input type="time" class="form-control" name="endTime" step="1800" required>
        </div>
        <div class="form-group">
            <label for="startDate">시작 날짜</label>
            <input type="date" class="form-control" name="startDate">
        </div>
        <div class="form-group">
            <label for="endDate">종료 날짜</label>
            <input type="date" class="form-control" name="endDate">
        </div>
    </div>
    <button type="button" class="btn btn-primary add-schedule">일정 추가</button>
    <button type="submit" class="btn btn-primary">제출</button>
</form>

<script>
    $(function () {
        $(".add-schedule").click(function () {
            var scheduleGroup = $(".schedule-group").first().clone();
            scheduleGroup.find("select, input").val("");
            $(".schedule-group").last().after(scheduleGroup);
        });
    });
</script>

구글링을 통해서 훌륭한 form 양식과 script를 참고할 수 있었다.
일정추가를 누르면 추가로 양식이 또 나오게 코드를 한 것 같다.

{
  doctorId: '1',
  dayOFTheWeek: [ '1', '1' ],
  startTime: [ '11:00', '08:00' ],
  endTime: [ '12:30', '16:30' ],
  startDate: [ '2023-03-15', '2023-03-15' ],
  endDate: [ '2023-03-31', '2023-03-31' ]
}


일단 데이터를 2번 제출하니 이런식으로 서버로 전송이 되는 것을 확인했다.
이제 이것을 서버에서 어떤식으로 가공할지? 그것을 생각해야할 것 같다.

const workingtime = [
  {
      doctorId: 1, dayOFTheWeek: 1, 
      startTime: '11:00', endTime: '12:30', 
      startDate:'2023-03-15', endDate: '2023-03-31'
  },
  {
      doctorId: 1, dayOFTheWeek: 2, 
      startTime: '13:00', endTime: '16:30', 
      startDate:'2023-03-15', endDate: '2023-03-31'
  },
  
];

이렇게 만드는게 내 최종목표이다.
이것을 서버에서 어떻게 가공할지… 이제 그런 로직을 짜야하는데 일단 map 함수를 한번 사용해보려고 한다.


--- 프론트 --- 

// 클라이언트로부터 받은 데이터 
const clientdata = {
  doctorId: '1',
  dayOFTheWeek: [ '1', '1' ],
  startTime: [ '11:00', '08:00' ],
  endTime: [ '12:30', '16:30' ],
  startDate: [ '2023-03-15', '2023-03-15' ],
  endDate: [ '2023-03-31', '2023-03-31' ]
}; 
---

--- 서버 --- 

const workingTime = []; // 가공된 데이터를 저장할 배열 

// 여기서 dayOFTheWeek.length로 설정한 이유는 전체 길이가 doctorId는 고정이여서 다르기 때문
for (let i=0; i< data.dayOFTheWeek.length; i++) {
  const data = {
      doctorId: parseInt(clientdata.doctorId), // 문자열을 정수형으로 변환
      dayOFTheWeek: parseInt(clientdata.dayOFTheWeek[i]), // 문자열을 정수형으로 변환
      startTime: clientdata.startTime[i],
      endTime: clientdata.endTime[i],
      startDate: clientdata.startDate[i],
      endDate: clientdata.endDate[i]
  };
  workingTime.push(data);
}

서버쪽 코드는 짰는데 문제는 프론트였다.

프론드 쪽에서 data를 보낼 때 한번에 많은 data를 보내야 하는데 첫번째 data만 보내는 것이다.

[
  {
    fieldname: 'images',
    originalname: 'api.jpg',
    encoding: '7bit',
    mimetype: 'image/jpeg',
    destination: 'uploads/',
    filename: '301e3b9b884af0985be99017351aed63',
    path: 'uploads\\301e3b9b884af0985be99017351aed63',
    size: 30647
  },
  {
    fieldname: 'images',
    originalname: 'bird.jpg',
    encoding: '7bit',
    mimetype: 'image/jpeg',
    destination: 'uploads/',
    filename: '10190dd1f14a90c858c37da915cea79a',
    path: 'uploads\\10190dd1f14a90c858c37da915cea79a',
    size: 8127
  },
  {
    fieldname: 'images',
    originalname: 'dorami.jpg',
    encoding: '7bit',
    mimetype: 'image/jpeg',
    destination: 'uploads/',
    filename: '6512606974d72e9d69bc6e0584cfe8bc',
    path: 'uploads\\6512606974d72e9d69bc6e0584cfe8bc',
    size: 11929
  }
]

이미지 파일을 여러개 업로드를 할 경우 req.files 콘솔로 찍어보면 위의 json 처럼 나온다.
이것을 아래와 같이 나타내고 싶은데…


const url = [
  {
      hospitalId: 1, 
      url:'https://moduhospital.s3.amazonaws.com/doctors/1678812366596_myprofile.jpg', 

  },
  {
      hospitalId: 1, 
      url:'https://moduhospital.s3.amazonaws.com/doctors/1678812366596_myprofile.jpg', 
  },
  {
      hospitalId: 1, 
      url:'https://moduhospital.s3.amazonaws.com/doctors/1678812366596_myprofile.jpg', 
  },
];
// 이미지 여러개 업로드 하기 
files.forEach((file, index) => {
        const fileContent = fs.readFileSync(file.path);
        const filename = `${Date.now()}_${file.originalname}`;
        const params = {
            Bucket: env.AWS_BUCKET_NAME,
            Key: `doctors/${filename}`,
            Body: fileContent,
            ContentType: file.mimetype,
        };

    try{
      const data = s3.upload(params).promise();
      fs.unlinkSync(file.path);
      return data.Location
    }catch (err) {
            console.error(err);
            throw new Error('Failed to upload image to S3');
        }
})

위에 같이 코드를 짜서 이미지를 업로드 하면 어떨까? 했는데 일단 성공은 했다. 그런데 return 값으로 data를 가져오려고 했으나 forEach를 벗어나서 return 으로 data.Location이 정의가 되지 않았다.

그래서 여러 가지 방면으로 찾아봤으나 스스로 생각은 하지 못했고 chatgpt를 이용했다.
그랬더니 이런 코드를 짜주었는데…

  const promises = files.map((file, index) => {
            const fileContent = fs.readFileSync(file.path); 
            const filename = `${Date.now()}_${file.originalname}`;
            const params = {
                Bucket: env.AWS_BUCKET_NAME,
                Key: `doctors/${filename}`,
                Body: fileContent,
                ContentType: file.mimetype,
              };
              return new Promise((resolve, reject) => {
                s3.upload(params, (err, data) => {
                    if (err) {
                        console.error(err);
                        reject(new Error(`Failed to upload image ${index + 1} to S3`));
                    } else {
                        console.log(`File ${index + 1} uploaded successfully. ${data.Location}`);
                        fs.unlinkSync(file.path);
                        resolve(data.Location);
                    }
                })
              })
        });

Promise() 함수를 이용한 코드를 짜주었는데… 현재의 나라면 절대 생각지 못할 코드이다.
일단 내가 이해한 바로는 Promise() 함수를 이해해야하는데…
Promise는 비동기적인 작업에서 결과를 반환할 때 사용하는 자바스크립트 객체이다.
이 때 비동기 작업이라는 말이 많이 나온다. 비동기 작업이란, 코드 실행 도중에 다른 작업을 수행하는 것으로 대표적으로 파일 로딩, API 호출 등이 있다. 이러한 작업들은 결과가 나오는 시간이 일정하지 않기 때문에 일반적인 동기적인 작업과 달리 기다리지 않고 다음 작업을 수행하게 된다.


Promise는 이런 비동기 작업의 결과를 다루기 위한 개념이다. 작업의 성공 혹은 실패에 따라 각각의 결과를 반한다. 3가지 상태를 가지는데 pending => 작업 수행 상태, fulfilled => 작업 완료, rejected=> 작업 실패, 이렇게 된다.
또 사용되는 메소드로 .then 과 .catch가 있다. 작업 성공시 then() 메소드, 실패시 catch() 메서드를 호출한다.
아래는 공식문서에서 참고한 예제 자료이다.

let myFirstPromise = new Promise((resolve, reject) => {
  // 우리가 수행한 비동기 작업이 성공한 경우 resolve(...)를 호출하고, 실패한 경우 reject(...)를 호출합니다.
  // 이 예제에서는 setTimeout()을 사용해 비동기 코드를 흉내냅니다.
  // 실제로는 여기서 XHR이나 HTML5 API를 사용할 것입니다.
  setTimeout( function() {
    resolve("성공!")  // 와! 문제 없음!
  }, 250)
})

myFirstPromise.then((successMessage) => {
  // successMessage는 위에서 resolve(...) 호출에 제공한 값입니다.
  // 문자열이어야 하는 법은 없지만, 위에서 문자열을 줬으니 아마 문자열일 것입니다.
  console.log("와! " + successMessage)
});

  1. new Promise를 통해서 새로운 Promise 객체를 생성한다. => 이 때 인자로 resolve, reject를 매개변수로 받는 함수를 전달한다.
  2. 비동기적 작업이 성공할 경우 resolve 함수를 호출하여 작업 결과를 반환 한다. 실패할 경우 reject함수를 호출하여 에러를 반환한다.
  3. myFirstPromise.then()을 사용해서 Promise 객체의 작업 결과를 처리한다. 이 함수는 인자로 resolve 함수에서 반환된 “성공”을 받아서 console.log 출력은 “와!성공!” 이 된다.
    • 이때 .then() 메소드는 Promise 객체에서 반환되는 메소드 중 하나이다. 그래서 Promise.then()을 호출하면 이전에 작업이 성공했을 때 실행할 콜백 함수를 등록할 수 있다.
    • 콜백함수란?
      다른 함수에게로 인자로 전달되는 함수를 말한다. 어떤 함수 A가 인자로 전달받은 함수 B를 다른 시점에 호출하는 경우, 그 함수 B를 콜백 함수라고 한다.
      예를 들면 웹브라우저에서 서버로 부터 데이터를 받아오는 작업은 시간이 오래 걸릴 수 있다. 따라서 이 작업을 비동기적으로 처리하기 위해 콜백함수를 사용할 수 있다. 이 때 서버로 부터 데이터를 받아온 후에 해당 데이터를 가지고 처리해야 하는 작업을 콜백 함수로 전달 해 놓으면, 데이터가 도착한 후에 콜백함수가 호출 되어 처리할 수 있다.

Promise 공식문서

생각정리3

다시 이미지 업로드를 생각해보자.

console.log(imageurls) // Promise.all를 통해 받은 데이터 
[
  'https://moduhospital.s3.amazonaws.com/doctors/1678890290776_api.jpg',
  'https://moduhospital.s3.amazonaws.com/doctors/1678890290883_bird.jpg',
  'https://moduhospital.s3.amazonaws.com/doctors/1678890290888_dorami.jpg',
  'https://moduhospital.s3.amazonaws.com/doctors/1678890290892_HominJoo.jpg'
]

이것을 잘 가공해서 한번에 data를 입력해야한다.
그전에 객체와 배열에 대해서 다시 공부하고 넘어가자
객체 안에는 여러 속성(property)이 있고 중괄호{}로 표현하고, 속성은 쉼표로 구분한다.

let person = {
  name: "John",
  age: 30,
  address: "123 Main St"
};

이 객체는 person이라는 변수에 3개의 속성이 있다. 각 속성은 name, value로 나뉘어져있고 “:” 으로 구분됨.

배열은(array)은 순서가 있는 값의 리스트이다. 배열 안에는 요소(element)가 있고, index로 구분한다. 배열은 대괄호 [] 로 묶어서 표현하고, 요소는 쉼표로 구분한다.

let fruits = ["apple", "banana", "orange"];
const url = [];
for (let i = 0; i < imageurls.length; i++) {
  const data = {
    hospitalId: hospitalId,
    url: imageurls[i]
  };
  url.push(data);
}

이 스크립트를 넣음으로써 잘 해결되었다~ ㅎㅎ;

생각정리4

// AJAX를 통해 데이터를 불러오는 함수
		function loadProfileCards() {
			var xhttp = new XMLHttpRequest();
			xhttp.onreadystatechange = function() {
				if (this.readyState == 4 && this.status == 200) {
					// 데이터를 JSON 객체로 파싱
					var data = JSON.parse(this.responseText);
					// 카드 생성 함수 호출
					createProfileCards(data);
				}
			};
			xhttp.open("GET", "/api/hospitals/information/doctor", true);
			xhttp.send();
		}
		
		// 카드를 생성하는 함수
		function createProfileCards(data) {
            var container = document.getElementById("cards-container");
			for (let i = 0; i < data.length; i++) {
				var card = document.createElement("div");
				card.className = "profile-card";
				var image = document.createElement("img");
				image.src = data[i].image;
				image.alt = "프로필 사진";
				var name = document.createElement("h1");
				name.textContent = data[i].name;
				var contents = document.createElement("p");
				contents.textContent = data[i].contents;
				var editButton = document.createElement("button");
				editButton.textContent = "의사 수정하기";
				editButton.onclick = function() {                   
                    location.href = `/doctorEdit/?doctorId=${data[i].doctorId}`
				};
				card.appendChild(image);
				card.appendChild(name);
				card.appendChild(contents);
				card.appendChild(editButton);
				container.appendChild(card);
			}
		}
		
		// 페이지 로드 시 카드를 로드하는 함수 호출
		loadProfileCards();

오늘 튜터님 한테 배운거 작성

코드 디플로이

ssh 배포의 단점 : github에 ssh key를 올려놔야함 , (보안 , config 파일 github 올리지 않음)

코드 디플로이 동작 방식

⇒ 우선 리포에 있는 코드를 압축을 해서 s3에다가 올림.

⇒ 코드 디플로이가 s3에 있는 압축된 코드를 개발서버?에 압축을 해제함(

내 레포 → s3 → 코드 디플로이 → 개발서버)

⇒ 코드 디플로이를 설정해야함 (어떤 s3를 쓸지)

위에 것을 자동으로 하기위해

⇒ 스크립트 shell 필요

zip 내 코드

aws s3 upload 압축파일

aws codedeploy 압축파일 개발서버

내일 할일

  • 프로젝트 이어서 하기

참고자료