I think it can be expressed visually quite good:
look at this code and the plots
import numpy as np
import matplotlib.pyplot as plt
k = 1
N = 50
time = np.arange(0.,N,1)/N # time in seconds
signal = np.exp(-1j*2*np.pi*k*time)
fig, axs = plt.subplots(2)
centroid = np.sum(signal)/N
axs[0].plot(signal.real, signal.imag, 'o')
axs[0].plot(centroid.real, centroid.imag, 'o')
axs[0].set_ylabel('Imaginary')
axs[0].set_xlabel('Real')
axs[1].plot(time, signal.real)
axs[1].plot(time, signal.imag, 'orange')
axs[0].set_ylabel('Imaginary')
axs[0].set_xlabel('Real')
plt.show()
which looks like
When you set higher values of $k$ you can see the following effect
Feel free to play around with these parameters.
for example $k = 20$
We can see for higher $k$ it starts rotating more often around $2 \pi$ so the wave is getting faster. We can also see that we have less points fitting our plot which comes from the sampling rate.
Feel free to play around with the parameters a little bit.
So with all thins in mind let's have a look at the dft.
The DFT can be interpreted as the inner product of two functions (it literally can be compared to the dot product of two vectors, since we are comparing all pairs $(x_i, y_i)$ and checking for their correlation.
Now what the DFT basically does is lying different complex waves into the graph of the original wave and computes it's correlation regarding to different frequencies. A nice property of the complex inner product is that it is invariant to the phase of the complex wave.
So to visualize this I came up with this code:
# create the signal
srate = 4000 # hz
# points from 0 to 2 in 1/srate steps
time = np.arange(0.,5,1/srate) # time vector in seconds
pnts = len(time) # number of time points 2000 here
# signal = np.cos(2*np.pi*1000*time - np.pi/2) + 2*np.cos(2*np.pi*2000*time + np.pi/2)
signal = np.cos(2*np.pi*1000*1/4000*time - np.pi/2) + 2*np.cos(2*np.pi*2000*1/4000*time + np.pi/2)
# prepare the Fourier transform
# fourTime is from 0 to N-1
fourTime = np.array(range(pnts))/pnts
fCoefs = np.zeros((len(signal)),dtype=complex)
# freq = random.randint(0, pnts)
k = 3
# create complex sine wave
csw = np.exp( -1j*2*np.pi*freq*fourTime )
# compute dot product between sine wave and signal
fCoefs[freq] = np.sum(np.multiply(signal,csw) ) / pnts
plt.plot(time, signal)
plt.plot(time, csw.real)
print(np.abs(fCoefs[freq]))
You can pluck in different values for $k$ and check its correlation with the original wave.
So the interesting point here is that I used time = np.arange(0.,5,1/srate)
for the original signal but foturTime = np.array(range(pnts))/pnts
for computing the values of the complex sine wave and used time
for plotting both.
The important point here is that in the original formula we just put in value the $n$ which is the same for both waves and since we are working with discrete signals there is no time involved anymore. It is just sampling rate and number of points now. You can check out this answer to see how it can be related back to the original sampling rate.
Hope that helps a little bit building some intuition!