diff --git a/src/apis/OpenAI.js b/src/apis/OpenAI.js
new file mode 100644
index 0000000..365f88e
--- /dev/null
+++ b/src/apis/OpenAI.js
@@ -0,0 +1,21 @@
+//AI api 연결
+
+import { Axios } from '../utils';
+
+
+export const AnalyzeAI = async (token, params) => {
+ try {
+ const res = await Axios.post('/api/v1/ai/analyze', params, {
+ headers: {
+ Authorization: `Bearer ${token}`,
+ 'Content-Type': 'application/json'
+ },
+ });
+
+ return res.data;
+ } catch (e) {
+ if (e instanceof Error) {
+ throw new Error('analyzeAI Error', e);
+ }
+ }
+};
diff --git a/src/components/common/input/MessageInput.js b/src/components/common/input/MessageInput.js
new file mode 100644
index 0000000..6c79b71
--- /dev/null
+++ b/src/components/common/input/MessageInput.js
@@ -0,0 +1,231 @@
+import { useEffect, useRef, useState } from 'react';
+import styled from 'styled-components';
+
+const AIMessageInput = ({ onSendMessage }) => {
+ const [isOpen, setIsOpen] = useState(false);
+ const [message, setMessage] = useState('');
+ const [isSending, setIsSending] = useState(false);
+ const textareaRef = useRef(null);
+ const modalRef = useRef(null);
+
+ // 텍스트 영역 높이 자동 조절
+ useEffect(() => {
+ if (textareaRef.current && isOpen) {
+ textareaRef.current.style.height = 'auto';
+ textareaRef.current.style.height = `${Math.min(textareaRef.current.scrollHeight, 200)}px`;
+ }
+ }, [message, isOpen]);
+
+ // 모달 외부 클릭시 닫기
+ useEffect(() => {
+ const handleClickOutside = (event) => {
+ if (modalRef.current && !modalRef.current.contains(event.target)) {
+ closeModal();
+ }
+ };
+
+ if (isOpen) {
+ document.addEventListener('mousedown', handleClickOutside);
+ }
+
+ return () => {
+ document.removeEventListener('mousedown', handleClickOutside);
+ };
+ }, [isOpen]);
+
+ // 모달 열기
+ const openModal = () => {
+ setIsOpen(true);
+ // 모달이 열린 후 텍스트 영역에 포커스
+ setTimeout(() => {
+ if (textareaRef.current) {
+ textareaRef.current.focus();
+ }
+ }, 100);
+ };
+
+ // 모달 닫기
+ const closeModal = () => {
+ setIsOpen(false);
+ setMessage('');
+ };
+
+ // 메시지 전송 처리
+ const handleSendMessage = () => {
+ if (message.trim() && !isSending) {
+ setIsSending(true);
+
+ // 메시지 전송 처리
+ if (onSendMessage) {
+ onSendMessage(message);
+ }
+
+ // 입력 초기화 및 상태 업데이트
+ setMessage('');
+ setIsSending(false);
+
+ // 모달 닫기
+ closeModal();
+ }
+ };
+
+ // 엔터 키 처리 (Shift+Enter 줄바꿈, Enter 전송)
+ const handleKeyDown = (e) => {
+ if (e.key === 'Enter' && !e.shiftKey) {
+ e.preventDefault();
+ handleSendMessage();
+ }
+ };
+
+ return (
+ <>
+ {/* 메뉴 버튼 */}
+
+
+
+
+
+
+ {/* 모달 오버레이 */}
+
+
+ setMessage(e.target.value)}
+ onKeyDown={handleKeyDown}
+ placeholder="메시지를 입력하세요..."
+ rows={1}
+ />
+
+
+
+
+
+
+ >
+ );
+};
+
+export default AIMessageInput;
+
+const ModalOverlay = styled.div`
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background-color: rgba(0, 0, 0, 0.5);
+ display: ${props => props.isOpen ? 'flex' : 'none'};
+ justify-content: center;
+ align-items: center;
+ z-index: 1000;
+`;
+
+// 메인 컨테이너
+const InputContainer = styled.div`
+ width: 90%;
+ max-width: 600px;
+ border: 1px solid #e0e0e0;
+ border-radius: 12px;
+ background-color: #ffffff;
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
+ overflow: hidden;
+ position: relative;
+ animation: ${props => props.isOpen ? 'slideUp 0.3s ease-out' : 'none'};
+
+ @keyframes slideUp {
+ from {
+ opacity: 0;
+ transform: translateY(20px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+ }
+`;
+
+// 메시지 입력 영역
+const MessageInput = styled.textarea`
+ width: 100%;
+ min-height: 60px;
+ max-height: 200px;
+ padding: 16px 60px 16px 16px;
+ border: none;
+ outline: none;
+ resize: none;
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
+ font-size: 16px;
+ line-height: 1.5;
+ background: transparent;
+
+ &::placeholder {
+ color: #9e9ea7;
+ }
+`;
+
+// 전송 버튼
+const SendButton = styled.button`
+ position: absolute;
+ bottom: 12px;
+ right: 12px;
+ width: 38px;
+ height: 38px;
+ border-radius: 50%;
+ background-color: #5436DA;
+ color: white;
+ border: none;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ cursor: pointer;
+ transition: all 0.2s ease;
+
+ &:hover {
+ background-color: #4527D0;
+ }
+
+ &:disabled {
+ background-color: #DADCE0;
+ cursor: not-allowed;
+ }
+
+ svg {
+ width: 18px;
+ height: 18px;
+ fill: white;
+ }
+`;
+
+// 메뉴 버튼 (세로 점 세개)
+const MenuButton = styled.button`
+ width: 40px;
+ height: 40px;
+ border-radius: 50%;
+ background-color: #f0f0f0;
+ border: none;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ cursor: pointer;
+ transition: all 0.2s ease;
+
+ &:hover {
+ background-color: #e0e0e0;
+ }
+
+ .dot {
+ width: 4px;
+ height: 4px;
+ border-radius: 50%;
+ background-color: #666;
+ margin: 2px 0;
+ }
+`;
\ No newline at end of file