동아리&프로젝트

[피로그래밍] 헬스턴트의 프론트로 살아남기

nanhe 2025. 8. 18. 22:16

안녕하세요, 규북입니다!

첫 포스트네요. 이번 글에서는 모든 게 처음이었던 피로그래밍 프로젝트 경험을 기록해보려고 합니다.

 

저는 2025.07.26 ~ 2025.08.19 동안 ‘헬스턴트’ 팀에서 프로젝트를 진행했습니다.
비전공자로 피로그래밍에 들어와 정말 많은 것을 배우고, 좋은 팀원들을 만나 행복하게 프로젝트를 완주할 수 있었습니다.♥️

 

혹시 피로그래밍이라는 이름을 들어보신 적 있나요?
피로그래밍은 웹 개발을 처음 접하는 대학생들을 위한 연합 동아리입니다.

방학 동안 매주 3회의 세션을 통해 웹 개발의 기초부터 프로젝트 협업까지 경험할 수 있고, 그 과정에서 성장과 협력의 가치를 나눌 수 있습니다. 2014년에 설립되어 지금까지 꾸준히 이어져 온 동아리랍니다.

 

저는 그중 23기 비전공자로 참여했습니다.

회고록은 따로 작성할 예정이라 이번 글에서는 추억이나 느낀 점은 길게 적지 않으려 합니다.

그래도 한 줄로 정리하자면, 2025년 여름은 제게 피로그래밍이 전부였던 시간이라고 말할 수 있을 것 같아요.

정말 열정을 갈아넣었고, 그만큼 값진 배움을 얻을 수 있었던 동아리였습니다ㅎㅎ

 

이제 본론으로 돌아와 개발 관련 이야기를 적어보겠습니다.
저는 헬스턴트에서 프론트엔드 파트로 참여했습니다.
JS는 피로그래밍에 들어오면서 처음 접했는데요, 개발자분들에겐 당연하게 보일 수 있는 부분도 저에겐 꽤 어려운 지점들이 있었습니다. 그래서 이번 글에서는 제가 어려워했던 내용들을 위주로 정리해보려 합니다.


가장 열심히 공부했고, 그 과정에서 얻은 것들을 정리해보겠습니다.

정말 많지만, 대표적으로 가장 뿌듯한 두 가지를 소개할게요!

 

1) 모달 창 구현

웹이지만 앱 뷰처럼 보이도록 재현했고,

프로젝트 규모가 커서 HTML 파일도 많았기 때문에 로그인 유도, 안내, 팝업은 모두 모달로 구현하기로 했습니다.

 

사실 모달이라는 개념도 이번 프로젝트를 하면서 처음 알게 되었습니다.

로그인 유도 모달은 다른 프론트 멤버가 고생해줬고, 저는 상세 검색 페이지(search) 에서 정렬과 범위 설정에 사용했고, 제품 상세 페이지(products) 에서는 판매처 이동 URL을 만들기 위해 활용했습니다.

그 중 판매처 이동 URL 관련 내용을 대표적으로 보여드릴게요

<div id="shareModal" class="share-modal" style="display: none;">
  <div class="share-modal-content">
    <div class="share-modal-header">
      <h3>공유하기</h3>
      <button class="close-btn" onclick="closeShareModal()">×</button>
    </div>
    <div class="share-modal-body">
      <div class="url-section">
        <input type="text" id="shareUrl" value="{% if product.shop_url %}{{ product.shop_url }}{% else %}{{ request.build_absolute_uri }}{% endif %}" readonly>
        <button class="copy-url-btn" onclick="copyUrl()">URL복사</button>
      </div>
    </div>
  </div>
</div>
function openShareModal() {
  document.getElementById('shareModal').style.display = 'flex';
}

function closeShareModal() {
  document.getElementById('shareModal').style.display = 'none';
}

function copyUrl() {
  const urlInput = document.getElementById('shareUrl');
  urlInput.select();
  document.execCommand('copy');
  
  // 복사 완료 피드백
  const copyBtn = document.querySelector('.copy-url-btn');
  const originalText = copyBtn.textContent;
  copyBtn.textContent = '복사완료!';
  copyBtn.style.background = '#56AAB2';
  
  setTimeout(() => {
    copyBtn.textContent = originalText;
    copyBtn.style.background = '#6c757d';
  }, 2000);
}

// 모달 외부 클릭 시 닫기
document.getElementById('shareModal').addEventListener('click', function(e) {
  if (e.target === this) {
    closeShareModal();
  }
});

AI와 함께 열심히 찾아내고 공부해서 만든 복사 링크 모달입니다. 실제로 이렇게 보입니다~ 정말 뿌듯합니다ㅎㅎ


 

2) 차트 구현 (Chart.js)

저희 서비스는 실사용자의 식단 데이터를 분석하는 기능이 필요했습니다.
사용자의 경험을 더 좋게 만들려면 데이터를 보기 쉽게 시각화하는 게 중요하다고 생각했고, 그래서 차트를 도입했습니다.

차트 제작도 처음이라 공부가 많이 필요했는데, 다행히 Chart.js를 활용해 원하는 화면을 구현할 수 있었습니다.

<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>

document.addEventListener('DOMContentLoaded', function() {
  const ctx = document.getElementById('calorieChart').getContext('2d');
  
  // 그라디언트 생성
  const gradient = ctx.createLinearGradient(0, 0, 0, 280);
  gradient.addColorStop(0, '#56AAB2');
  gradient.addColorStop(1, 'rgba(217, 217, 217, 0)');
  
  // Django 템플릿에서 데이터 가져오기
  const chartData = {
    labels: [
      {% for data in calorie_trend.daily_data|slice:":8" %}
        '{{ data.date|date:"m/d" }}'{% if not forloop.last %},{% endif %}
      {% endfor %}
    ],
    datasets: [{
      label: '칼로리 섭취량',
      data: [
        {% for data in calorie_trend.daily_data|slice:":8" %}
          {{ data.calorie|floatformat:0 }}{% if not forloop.last %},{% endif %}
        {% endfor %}
      ],
      borderColor: '#56AAB2',
      backgroundColor: gradient,
      borderWidth: 3,
      fill: true,
      tension: 0.4,
      pointBackgroundColor: function(context) {
        const value = context.parsed.y;
        const minValue = {{ calorie_trend.min_data.calorie|floatformat:0 }};
        const maxValue = {{ calorie_trend.max_data.calorie|floatformat:0 }};
        
        if (value === minValue) return '#56AAB2';
        if (value === maxValue) return '#EF4444';
        return '#56AAB2';
      },
      pointBorderColor: '#ffffff',
      pointBorderWidth: 2,
      pointRadius: 6,
      pointHoverRadius: 8
    }]
  };

  const config = {
    type: 'line',
    data: chartData,
    options: {
      responsive: true,
      maintainAspectRatio: false,
      plugins: {
        legend: {
          display: false
        },
        tooltip: {
          backgroundColor: 'rgba(0, 0, 0, 0.8)',
          titleColor: '#ffffff',
          bodyColor: '#ffffff',
          borderColor: '#7CC7C2',
          borderWidth: 1,
          callbacks: {
            label: function(context) {
              return context.parsed.y + ' kcal';
            }
          }
        }
      },
      scales: {
        x: {
          grid: {
            color: '#f0f0f0',
            drawBorder: false
          },
          ticks: {
            font: {
              size: 12
            },
            color: '#666'
          }
        },
        y: {
          grid: {
            color: '#f0f0f0',
            drawBorder: false
          },
          ticks: {
            font: {
              size: 12
            },
            color: '#666',
            callback: function(value) {
              return value + ' kcal';
            }
          }
        }
      },
      interaction: {
        intersect: false,
        mode: 'index'
      }
    }
  };

  new Chart(ctx, config);
});

제가 제작한 화면입니다

 

제가 만든 화면은 이렇게 보입니다.
어떤가요? 정말 잘 보여서 너무 뿌듯합니다ㅎㅎ

제 애정이 가득 담긴 페이지라 앞으로도 영원히 기억될 것 같아요. 볼 때마다 감동의 눈물이 날 정도입니다 ㅜㅜ


프론트를 하면서 가장 힘들었던 것이 무엇이냐고 묻는다면, 저는 뒤늦게 진행한 리팩토링이라고 답할 것 같습니다.

CSS 관리와 공통 HTML 부분을 정리하기 위해 base.html, base.css 등 공통 파일을 분리해 한 곳에 모으고, 장고 HTML의 장점인 상속 기능을 활용해 다른 페이지들을 관리했습니다.

사실 처음부터 이런 방식을 전혀 생각하지 않았던 건 아닙니다. 하지만 페이지별로 화면이 다 다르다 보니 굳이 만들 필요가 있을까 싶었죠. 그런데… 생각보다 페이지 수가 너무너무 많아져서 관리하기가 힘들어졌습니다. 그래서 “더 늦기 전에 시작하자” 하고 리팩토링을 진행했는데, 약 4시간 정도 소모했던 것 같아요. (어리석은 과거의 나…😂)

그래서 뼈저리게 느낀 base.html의 장점을 소개해보겠습니다.

  1. 공통 CSS를 수정할 때 원래라면 30개 파일을 전부 바꿔야 하지만, 이제는 한 번만 변경해도 되는 최고의 효율!
  2. 각각의 HTML 코드가 간결해져서 관리가 훨씬 편리!

정말 장점이 많습니다. 혹시 이 글을 보고 계신 미래의 초보 프론트님… 꼭 저처럼 뒤늦게 리팩토링하지 마시고, 처음부터 잘 설계하시길 바랍니다. 응원합니다! 🙌

또, 프로젝트를 하면서 가장 힘들었던 부분을 꼽으라면 저는 주저 없이 DB라고 말할 수 있을 것 같습니다. 물론 저는 직접 DB에 참여하지 않아 기술적인 부분을 깊게 적을 순 없지만, 수많은 역경과 고난을 이겨내고 결국 성공적으로 마무리해준 민지에게 정말 큰 박수를 보내고 싶습니다. 👏👏

 

그래도 제가 아는 부분을 조금 적어보자면, 저희 팀은 서버를 Naver Cloud Platform(NCP) 에서 운영했습니다. 피로그래밍은 네이버 클라우드와 연계되어 있어 20만 원 크레딧을 지원받을 수 있었는데, 이 덕분에 무료로 서버를 돌릴 수 있었습니다. 늘 많은 도움을 주신 네이버에게 정말 무한한 감사를 드립니다… (네이버 최고…! 사실 제 주 블로그도 원래 네이버랍니다 😆)


이상으로 프론트로서의 헬스턴트 기술 일기를 마무리하겠습니다.

우리의 헬스턴트 추억은 곧 올라올 피로그래밍 23기 회고록과 함께 낋여오겠습니다.
또, 면접 후기와 합격 후기까지 차근차근 총총총 작성해볼게요.

 

마지막으로 최종 발표회를 앞두고 있는데!

이 헬스턴트를 세상에 나올 수 있게 기획해주고, 늘 우리 생각해서 view짜주고 질문 다 받아준 우리 동수오빠 정말 고맙고,

DB에 고생하고 많은 고난과 역경이 있었는데 포기하지 않아준 우리 민지도 정말 고맙고,

마지막으로 같은 프론트로서 많은 밤을 함께 새고 프론트 모든걸 같이 해준 우리팀 최고의 성장캐 가원이 정말 고마워

헬스턴트 정말 수고 많았고.. 정말 수고많았어~ 

 

나답게 엔딩은 늘 사진으로

헬스턴트 최고