Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 119 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1 +1,120 @@
# FrontEnd

## 휴머니케어 - 실질적 독거노인을 위한 음성 상호작용 앱

**"혼자 계신 부모님이 걱정되시나요?"**
이 앱은 실질적으로 혼자 거주하시는 고령자를 위해 설계되었습니다.
보호자가 미리 지정한 키워드를 통해 질문을 음성으로 전달하고,
어르신은 그 IoT에 응답함으로써 일상 상태를 간단히 공유할 수 있습니다.
또한 보호자의 음성을 학습시켜 고령자에게 더 친근하고 정서적인 상호작용이 가능합니다.


### 프로젝트 실행 방법

#### 1. 프로젝트 클론
```bash
git clone https://github.com/HumaniCare/FrontEnd.git
cd FrontEnd
```

#### 2. 필요한 패키지 설치
```bash
npm install
```

#### 3. 개발 서버 실행
```bash
npm start
```
기본 포트는 `http://localhost:3000`입니다.


### 주요 의존성

다음은 `package.json` 기준으로 프로젝트에 사용된 주요 라이브러리입니다:

| 패키지명 | 설명 |
|---------|------|
| `react` | React 18 기반의 SPA 개발 |
| `react-router-dom` | 페이지 라우팅 처리 |
| `axios` | HTTP 통신 처리 (Spring/FASTAPI와 연동) |
| `mic-recorder-to-mp3` | 브라우저 마이크 녹음 및 mp3 변환 |
| `react-time-picker` | 시간 선택 기능 제공 |
| `@testing-library/react` 외 | 테스트용 도구들 |


### 음성 학습 기능

- 마이크 버튼을 누르면 음성이 녹음되고, mp3 형식으로 변환됩니다.
- 사용자가 직접 재생/삭제/전송할 수 있으며, 전송 시 FastAPI 서버로 POST 요청됩니다.
- 저장된 음성은 보호자 앱 또는 서버에서 활용 가능합니다.


### 기타 참고

- 녹음 전 `마이크 권한`을 요청합니다. 브라우저 설정에서 허용이 필요합니다.
- 실서비스 배포 시 HTTPS 환경과 CORS 설정 확인 필수입니다.


---

## HumaniCare – A Voice Interaction App for Elderly Living Alone

**"Worried about your parents living alone?"**
This application is designed for elderly individuals who live independently.
Guardians can set predefined questions that are delivered via voice to the elderly user,
who can then respond through a simple IoT interface.
Additionally, the app supports voice training using the guardian’s own voice, enabling more emotional and comforting interactions.

---

### How to Run the Project

#### 1. Clone the Repository
```bash
git clone https://github.com/HumaniCare/FrontEnd.git
cd FrontEnd
```

#### 2. Install Required Packages
```bash
npm install
```

#### 3. Start the Development Server
```bash
npm start
```

The default development URL is `http://localhost:3000`.

---

### Main Dependencies

The following are the key libraries used in the project based on `package.json`:

| Package | Description |
|---------|-------------|
| `react` | React 18 for single-page application development |
| `react-router-dom` | Routing between pages |
| `axios` | HTTP communication with Spring/FastAPI servers |
| `mic-recorder-to-mp3` | Recording audio from the browser and converting it to MP3 |
| `react-time-picker` | Provides time selection UI |
| `@testing-library/react` etc. | Tools for component testing |

---

### Voice Training Feature

- Users can record audio by clicking the mic button. The recording is converted to an MP3 file.
- The user can preview, delete, or upload the file.
- Upon uploading, the audio file is sent to the FastAPI server via a `POST` request.
- The saved voice data can later be used in the guardian’s app or backend services.

---

### Notes

- Microphone permissions are requested before recording. Please allow access in your browser.
- For production deployment, make sure to use **HTTPS** and properly configure **CORS** on the backend.
49 changes: 49 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"@testing-library/react": "^16.2.0",
"@testing-library/user-event": "^13.5.0",
"axios": "^1.8.4",
"mic-recorder-to-mp3": "^2.2.2",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-router-dom": "^6.17.0",
Expand Down
55 changes: 52 additions & 3 deletions src/components/MicButton.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,55 @@
import React from "react";
import React, { useState } from "react";
import MicRecorder from "mic-recorder-to-mp3";
import { FASTAPI_API_URL } from "../constants/api";

const recorder = new MicRecorder({ bitRate: 128 });

const MicButton = () => {
const [isRecording, setIsRecording] = useState(false);

const handleMicClick = () => {
// 마이크 명시적으로 권한 요청
navigator.mediaDevices.getUserMedia({ audio: true })
.then(() => {
// 권한 허용됨
if (!isRecording) {
recorder.start().then(() => setIsRecording(true));
} else {
recorder.stop()
.getMp3()
.then(([buffer, blob]) => {
setIsRecording(false);
const file = new File(buffer, "voice.mp3", {
type: blob.type,
lastModified: Date.now(),
});

const formData = new FormData();
formData.append("file", file);

fetch(`${FASTAPI_API_URL}/upload`, {
method: "POST",
body: formData,
})
.then(res => res.json())
.then(data => {
console.log("업로드 완료:", data);
})
.catch(err => console.error("업로드 실패:", err));
})
.catch(e => console.error("녹음 종료 실패:", e));
}
})
.catch((err) => {
alert("마이크 권한이 필요합니다.");
console.error("마이크 권한 거부:", err);
});
};

const MicButton = ({onClick}) => {
return (
<button onClick={onClick} style={styles.micButton}>
<button onClick={handleMicClick} style={styles.micButton}>
<img src="/images/mic_icon.png" alt="Mic Icon" style={styles.micIcon} />
<p>{isRecording ? "녹음 중... 클릭하면 종료" : "마이크 누르기"}</p>
</button>
);
};
Expand All @@ -14,6 +60,9 @@ const styles = {
border: "none",
cursor: "pointer",
marginBottom: "20px",
display: "flex",
flexDirection: "column",
alignItems: "center"
},
micIcon: {
width: "60px",
Expand Down
3 changes: 2 additions & 1 deletion src/components/oauth/KakaoRedirectPage.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import React, { useEffect, useRef } from "react";
import { useLocation, useNavigate } from "react-router-dom";
import instance from "../../axios/TokenInterceptor";
import { SPRING_API_URL } from "../constants/api";
import { SPRING_API_URL } from "../../constants/api";


const KakaoRedirectPage = () => {
const location = useLocation();
Expand Down
2 changes: 0 additions & 2 deletions src/pages/FinalPage.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
// FinalPage.jsx
import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import Logo from "../components/Logo";
import { Logout } from "../axios/TokenInterceptor"; // 실제 axios 파일 경로에 맞게 수정

const FinalPage = () => {
const navigate = useNavigate();
const [showConfirm, setShowConfirm] = useState(false);

const handleLogoutClick = () => {
Expand Down
12 changes: 11 additions & 1 deletion src/pages/KeywordSelectionPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,17 @@ const KeywordSelectionPage = () => {
console.log("JSON 파싱 직전");
console.log(schedules);

const updatedSelected = JSON.parse(JSON.stringify(selected));
const updatedSelected = Object.fromEntries(
Object.entries(keywords).map(([category, list]) => [
category,
Object.fromEntries(
list.map((keyword) => [
keyword,
{ selected: false, time: "08:00", days: [] }
])
)
])
);

schedules.forEach(item => {
const [categoryPrefix, keyword] = item.scheduleTitle.split("_");
Expand Down
Loading