
Mr.강 & Ms.고
프로젝트 개요
부트캠프의 파이널 프로젝트로서, 머신러닝(svm, tf-idf, knn), 딥러닝(cnn)을 활용한 반려동물 친구 추천, 반려동물 추천, 반려동물 품종 분석 기능이 포함된 반려동물 종합 플랫폼을 개발했습니다.
기획 의도
COVID 19 이후 반려인이 1500만명으로 증가했습니다. 좀 더 재밌는 산책 방법을 알고 싶은 것, 같은 동네 반려인들과 소통하고 싶은 것, 편리한 반려용품 쇼핑을 한 곳에서 해결할 수 있도록 하는 플랫폼입니다.
핵심 컨텐츠
산책로:
경로 녹화를 통해 나의 이동경로를 기록하고 기록한 산책로를 확인할 수 있습니다. 현재 내 위치를 기준으로 주변 동물병원과 애완용품점 위치를 알 수 있습니다.
친구 찾기:
반려동물이 사는 곳, 종, 상세설명 분석을 통해 반려동 물 친구를 추천해드립니다. 친구추가를 통해 반려동물 친구를 만들고 1대1 쪽지기능을 사용할 수 있습니다
쇼핑몰:
반려동물의 건강한 삶을 위해 해외 브랜드를 엄선하여 좋은 상품을 보급하고 있습니다. 반려동물의 편의와 행복을 위한다는 사명으로 꾸준히 연구하고 있습니다.
수행 업무
- 머신러닝 및 딥러닝 총괄
- 강아지와 고양이 중 추천 기능
- 친구 추천 기능 구현
- 사진 속 반려동물 품종 분석
- RestAPI 를 활용한 Spring과 DJango 연동
- 딥러닝 학습 데이터 수집 및 회원 더미 데이터 생성 (RPA)
- 페르소나 작성
- DB 설계
시스템 아키텍처

유스케이스 / ERD


주요 기능 구현 설명
성향분석 동물추천 – 나는 과연 강아지와 고양이 중 무엇이 어울릴까?
Ajax를 이용해 유저가 설문조사에서 선택한 지문과 그 결과를 Django 서버와 JSON형태로 통신하게끔 했습니다.


1. 고양이 혹은 강아지를 키우는 사람들의 성향 데이터 수집을 위해 설문조사 작성 및 배포

2. 그룹별 성향 차이를 파악하여 대립관계 파악

3. 대립관계에 속하는 컬럼을 추출

4. Ensemble과 Grid Search을 이용해 선정된 이중 분류모델(Support Vector Machine) 생성 및 학습

5. 학습된 데이터를 바탕으로 강아지 혹은 고양이 중 어느 것이 어울리는 지 결과 예측
from django.db import models
import joblib
import numpy as np
from sklearn.preprocessing import StandardScaler
import pandas as pd
import pickle
def sur_user(**kwargs):
# 유저로부터 입력된 성향 정보를 모델이 읽을 수 있는 Numpy 배열로 정규화
user_json = kwargs
listv = []
listv.append(user_json['in_outv'])
listv.append(user_json['j_pv'])
listv.append(user_json['f_tv'])
listv.append(user_json['friendy_unfamv'])
listv.append(user_json['convent_openv'])
listv.append(user_json['simple_artisticv'])
round_age = round(int(user_json['agev']), -1)
print(round_age)
ra = [0 if int(round_age) != i else 1 for i in range(0, 110, 10)]
listv += ra
user = np.array(listv, dtype=np.float32)
print(user)
return user
def sur_dog_cat(user):
# StandardScaler 정규화된 모델을 로드해야 한다.
# 학습 시킨 모델을 Django에서 서비스 하기 위해 로드
load_svc = joblib.load('static/google_spreadsheet/final_svc.pkl')
scaler = joblib.load('static/google_spreadsheet/final_scaler2.pkl')
train_X = pickle.load(open('static/google_spreadsheet/X_train2.pkl', 'rb'))
test_X = pickle.load(open('static/google_spreadsheet/X_test2.pkl', 'rb'))
scaler.transform(train_X)
scaler.transform(test_X)
# 강아지 혹은 고양이 중 어느 것이 어울리는 지 결과 예측
mydata = scaler.transform(np.array([user]))
dog_cat = load_svc.predict(mydata)
return dog_cat친구 추천
각 상세설명의 코사인 유사도를 계산하고 TF-IDF 모델을 사용하여 반려동물과 비슷한 성향의 반려동물을 친구로 추천해주는 기능을 개발했습니다.

from logging import exception
from django.db import models
from config.models import My_Ora
import numpy as np
import pandas as pd
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
from friends_recomm.petty_job import tokenizer, get_recommend
class Friend_Recomm(My_Ora):
def get_recomm(self, member_id):
conn = self.get_conn()
res_df = pd.DataFrame()
try:
cv = pd.read_sql_query("""select animal_num,animal_img1,animal_name, member_id, animal_detail from (
select rownum a_num,a.animal_num,a.animal_img1,a.animal_name,a.member_id, a.animal_detail from (
select animal_num, animal_name, animal_img1, member_id, animal_detail from animal
where member_id in(
select member_id from member where member_addr = (
select member_addr from member
where member_id=:member_id)
and member_id not in (select friends_userid from friends where friends_status='승인' and friends_friendid=:member_id
union
select friends_friendid from friends where friends_status='승인' and friends_userid=:member_id))
and cate_species= (select cate_species from animal where member_id=:member_id)
) a
)""", params={"member_id": member_id}, con=conn)
print(cv)
my_pets_num = cv[cv['MEMBER_ID'] == member_id].ANIMAL_NUM.to_list()[0]
cv['ANIMAL_DETAIL'] = cv['ANIMAL_DETAIL'].astype(str)
# 한글 외에 문자를 제거하기
cv['ANIMAL_DETAIL_FITTABLE'] = cv['ANIMAL_DETAIL'].str.replace("[^가-힣 ]", "")
cv.drop_duplicates(['ANIMAL_DETAIL'], inplace=True)
cv.reset_index(drop=True,inplace=True)
# tokenizer란 함수로 전처리 한 후, 토큰화 된 값을 가지고 TF-IDF로 벡터화 시킨다.
tfidf = TfidfVectorizer(tokenizer=tokenizer)
try:
tfidf.fit(cv['ANIMAL_DETAIL_FITTABLE'])
tfidf_matrix = tfidf.transform(cv['ANIMAL_DETAIL_FITTABLE'])
# 코사인 유사도 계산
cosine_sim = cosine_similarity(tfidf_matrix, tfidf_matrix)
res_df = get_recommend(my_pets_num,cv,cosine_sim)
res_df=res_df.drop(['COSINVAL','ANIMAL_DETAIL_FITTABLE'],axis=1)
res_df.reset_index(drop=True, inplace=True)
except ValueError as ve:
res_df = cv[cv['MEMBER_ID'] != member_id]
except IndexError as ie:
print(ie)
return res_df1. 워드 임베딩으로 벡터화 된 문장들의 코사인 유사도 계산
import pandas as pd
def get_recommend(animal_num,cv,consine_sim):
indexdata = pd.Series(cv.index, index=cv['ANIMAL_NUM']).drop_duplicates()
idx = indexdata[animal_num]
# 코사인 유사도가 높은 순으로 리스트를 정렬
sim_scores = list(enumerate(consine_sim[idx]))# 나의 강아지 글과 비슷한 강아지 글을 비교하여 코사인 유사도를 리스트
sim_scores = sorted(sim_scores, key=lambda x: x[1], reverse=True)
sim_scores2 = []
# 자신의 코사인 유사도와 animal_num을 제외한 항목들을 리스트에 저장
for i in sim_scores:
for k, j in enumerate(i):
if k == 0: # 0은 인덱스 번호, 1 은 코사인 유사도
if j != idx: #인덱스 번호가 나의 인덱스 번호와 다를시에나 리스트에 인덱스 추가하여 준다.
sim_scores2.append(i)
sim_scores2 = sim_scores2[:8] # 여덟개까지 추천
animal_index = [i[0] for i in sim_scores2] #애니멀 인덱스
cos_va = [j for (i, j) in sim_scores2] # 코사인 유사도
# 자신을 제외하고 코사인 유사도로 정렬한 DataFrame을 반환하여 친구 추천 구현
# DataFrame 만들기
res_df = cv.iloc[animal_index] # 비슷한 detail을 순서대로 한 animal_index리스트, 이것을 순서대로 dataframe을 정렬한다.
res_df['COSINVAL'] = cos_va
return res_df2. Spring 서버에서 django 서버의 친구 추천 목록 API 호출
@Component
public class Friend_Recomm_API {
private HttpURLConnection http; // 요청을 보낼 url주소! => 192.168.0.11:9000/park/shop_recom
// 장고 서버에 요청을 보내는 메서드!
// 2.
public void request(String method, String headerName, String headerValue, JSONObject jsonData) throws IOException{
http.setRequestMethod(method);
http.setRequestProperty(headerName, headerValue);
// GET 데이터를 스트림으로 처리 후 서버에 전송
http.setDoOutput(true);
// 요청 전송
PrintWriter printWriter = new PrintWriter(new OutputStreamWriter(http.getOutputStream()));
printWriter.write(jsonData.toString());
printWriter.flush();
}
// 3.
public List<Map<String, Object>> response() throws IOException{
BufferedReader bufferedReader = null;
int status = http.getResponseCode();
if (status==HttpURLConnection.HTTP_OK) {
bufferedReader = new BufferedReader(new InputStreamReader(http.getInputStream(),"UTF-8"));
} else {
bufferedReader = new BufferedReader(new InputStreamReader(http.getErrorStream()));
}
String line;
StringBuffer response = new StringBuffer();
while((line = bufferedReader.readLine()) != null) {
response.append(line);
}
bufferedReader.close();
JSONArray jsonArray = new JSONArray(response.toString());
List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
if( jsonArray != null )
{
int jsonSize = jsonArray.length();
for( int i = 0; i < jsonSize; i++ )
{
Map<String, Object> map = getMapFromJsonObject(jsonArray.getJSONObject(i));
list.add( map );
}
}
return list;
}
// 3-1. - RestAPI형태로 받은 JSON형태의 응답을 List<Map> 형식으로 변환 후 서비스
@SuppressWarnings("unchecked")
public Map<String, Object> getMapFromJsonObject( JSONObject jsonObj ){
Map<String, Object> map = new HashMap<String, Object>();
int animal_num = jsonObj.getInt("ANIMAL_NUM");
String animal_name = jsonObj.getString("ANIMAL_NAME");
String member_id = jsonObj.getString("MEMBER_ID");
String animal_img1 = jsonObj.getString("ANIMAL_IMG1");
map.put("ANIMAL_NUM", animal_num);
map.put("ANIMAL_NAME", animal_name);
map.put("MEMBER_ID", member_id);
map.put("ANIMAL_IMG1", animal_img1);
return map;
}
}품종분석 - 이 고양이 혹은 강아지의 품종이 궁금해!
인공지능을 이용한 사진 속 품종 분석을 개발했습니다.

// django/final_project_BTeam/cnn_guess/templates/cnn_guess/cnn_guess_form.html
$('#wrtBtn').click(function(){
//document.form1.action='res';
const imageInput = $("#file1")[0];
// 파일을 여러개 선택할 수 있으므로 files 라는 객체에 담긴다.
console.log("imageInput: ", imageInput.files)
if(imageInput.files.length === 0){
alert("파일은 선택해주세요");
return;
}
const formData = new FormData();
formData.append("file1", imageInput.files[0]);
$.ajax({
url: "res",
type : "POST",
processData: false,
contentType: false,
data: formData,
dataType : "json",
success: function(data){
//console.log(data);
var result = JSON.parse(data).cnn_res;
console.log(JSON.parse(data).cnn_res);
//console.log(JSON.stringifydata["cnn_res"]);
$('#target').html(result);
}
});
//document.form1.submit();
});# django/final_project_BTeam/cnn_guess/views.py
import json
from django.http import HttpResponse, JsonResponse
from django.shortcuts import render
from django.views.decorators.csrf import csrf_exempt
from cnn_guess.models import Cnn_Model
# Create your views here.
def cnn_guess_form(request):
return render(request, "cnn_guess/cnn_guess_form.html")
@csrf_exempt
def cnn_guess_res(request):
file = request.FILES['file1']
cnn = Cnn_Model(file)
cate = cnn.cnn_model()
res = {}
res["cnn_res"] = cate
aa = json.dumps(res) # 제이슨으로!
aa = json.loads(aa)
response = JsonResponse(aa, json_dumps_params={'ensure_ascii': False}, safe=False)
return response구현 아키텍처

이미지 학습
1. CNN모델 레이어 생성 및 정규화된 이미지 학습
CNN모델 레이어 생성 및 softmax를 통해 다중분류모델로 컴파일 하여 정규화된 이미지를 학습시켰습니다.

2. Jupyter-notebook에서 만들었던 모델을 Django에서 로드 및 컴파일
# django/final_project_BTeam/cnn_guess/models.py
# model.json 파일 열기
class Cnn_Model():
cnt = 0
def __init__(self,img_path):
# 객체를 여러번 불러올 필요는 없으므로 최초에 한번 만들 놓겠음
if Cnn_Model.cnt == 0:
json_file = open("static/saved_models/catdog_model.json", "r")
loaded_model_json = json_file.read()
json_file.close()
# json파일로부터 model 로드하기
Cnn_Model.loaded_model = model_from_json(loaded_model_json)
# 로드한 model에 weight 로드하기
Cnn_Model.loaded_model.load_weights("static/saved_models/model.h5")
print("Loaded model from disk")
Cnn_Model.loaded_model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
Cnn_Model.categories = joblib.load("static/saved_models/categories.pkl")
Cnn_Model.cnt += 1
self.origindir = img_path3. 클라이언트로부터 업로드 된 이미지로 품종 분석
# django/final_project_BTeam/cnn_guess/models.py
class Cnn_Model():
def cnn_model(self):
# 이미지를 Numpy 배열로 변환 및 정규화
x_test = []
img = Image.open(self.origindir)
img = img.convert("RGB")
img = img.resize((64, 64))
data = np.asarray(img)
x_test.append(data)
X = np.array(x_test)
# 값을 예측 후 라벨 중 1에 가장 가까운 인덱스의 이름을 추출
X = X.astype(float) / 255
prediction = Cnn_Model.loaded_model.predict(X)
pp = np.argmax(prediction)
category = Cnn_Model.categories[pp]
return category이미지 학습을 위한 이미지 수집(RPA)
웹페이지를 크롤링 하여 이미지의 URL을 Data Table에 저장했습니다.

이후 폴더이름을 이용, 파일 이름 및 경로를 지정, Data Table에 저장한 이미지를 이용해 파일 경로로 다운로드


주요성과
데이터 증강과 클라우드 환경 도입으로 품종 분류 모델의 정확도 90% 초과 달성
문제 상황:
반려동물 품종 이미지는 방대한 양이 필요하지만, 로컬 CPU 환경에서 이를 학습시키다 보니 컴퓨팅 자원이 부족해 병목 현상이 발생했습니다. 또한, 수집 가능한 원본 데이터의 수량이 절대적으로 부족해 초기 모델의 예측 정확도가 목표치인 90%에 한참 못 미쳤습니다.
해결 방안:
먼저, 모델 학습 환경을 Google Colab의 클라우드 GPU로 전환하여 하드웨어 제약 문제와 훈련 속도를 크게 개선했습니다. 더불어, 데이터 부족 문제를 극복하고자 각도 변환, 이미지 반전 등 다양한 데이터 증강 기법을 적용해 기존 이미지를 가공했습니다. 이 과정을 통해 학습 데이터셋의 규모를 두 배 이상으로 확장하였습니다.
성과:
이러한 개선을 바탕으로 컴퓨팅 리소스의 한계를 효과적으로 극복하고, 모델 학습을 안정적으로 완료할 수 있었습니다. 결국 반려동물 품종 분류 모델의 예측 정확도 또한 목표였던 90%를 넘어서는 성과를 거둘 수 있었습니다. 본 경험을 통해 한정된 환경에서도 창의적인 방법으로 문제를 해결하는 역량을 키울 수 있었다고 생각합니다.
레슨런
비동기 SPA 기반 UI/UX 최적화 경험
비동기 방식의 SPA를 도입하여 사용자 성향에 맞는 반려동물 추천 서비스를 직접 구현하는 과정에서 프론트엔드 렌더링 최적화와 사용자 경험 개선을 위해 다양한 시도를 했으며, 실제 업무 현장에서 UI/UX를 깊이 있게 다루는 실력을 키울 수 있었습니다.
RPA를 이용한 데이터 수집 자동화
UiPath 등 RPA 도구를 적극 도입하여, 대규모 데이터 수집 프로세스를 자동으로 구축했습니다. 반복적이고 방대한 자료를 수집·정리하면서 자연스럽게 크롤링과 데이터 파이프라인 설계 능력을 높일 수 있었고, 실제 현장에서의 활용 방안도 몸소 익혔습니다.
NumPy를 활용한 실전 데이터 전처리
다양하게 수집된 비정형 데이터들을 서비스 요구사항에 맞도록 효율적으로 가공하고 정제하는 과정에서 NumPy 등 데이터를 다루는 기술을 익혔습니다. 실제 현장 데이터의 복잡성을 경험하며, 작업 효율성을 높일 수 있던 노하우를 쌓았습니다.
머신러닝/딥러닝 실무 적용
이진 분류 문제부터 KNN, CNN 기반의 다중 분류 모델까지 다양한 AI 알고리즘을 직접 실무에 적용해왔습니다. 이 과정에서 ML/DL 모델의 특성을 파악하고 서비스와 기술을 유기적으로 접목시키는 능력을 발전시켰습니다.
CORS 및 네트워크 이슈 해결
서로 다른 도메인 간 REST API 통신에서 발생하는 CORS 문제를 주도적으로 해결한 경험이 있습니다. 프록시 서버 설정, 프레임워크 차원의 세부 대응 등을 통해 네트워크 환경을 안정적으로 구축하고, 동시에 보안 정책에 대한 이해도 깊어졌습니
목표 달성과 책임 의식
기술적인 난이도가 높은 과제를 맡아 밤낮없이 몰입한 끝에, 초기에 계획했던 도전적인 목표를 모두 달성할 수 있었습니다. 이 과정에서 쉽지 않은 상황을 해결하는 문제 해결력과 집요함, 그리고 맡은 바에 대한 책임감을 몸소 배울 수 있었습니다.